// funmath.cpp -- MUX math function handlers.
//
// $Id: funmath.cpp,v 1.3 2005/07/14 04:40:54 rmg Exp $
//
// MUX 2.4
// Copyright (C) 1998 through 2005 Solid Vertical Domains, Ltd. All
// rights not explicitly given are reserved.
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include "functions.h"
#include "funmath.h"
#include "sha1.h"
#ifdef HAVE_IEEE_FP_FORMAT
const char *mux_FPStrings[] = { "+Inf", "-Inf", "Ind", "NaN", "0", "0", "0", "0" };
#define MUX_FPGROUP_PASS 0x00 // Pass-through to printf
#define MUX_FPGROUP_ZERO 0x10 // Force to be zero.
#define MUX_FPGROUP_PINF 0x20 // "+Inf"
#define MUX_FPGROUP_NINF 0x30 // "-Inf"
#define MUX_FPGROUP_IND 0x40 // "Ind"
#define MUX_FPGROUP_NAN 0x50 // "NaN"
#define MUX_FPGROUP(x) ((x) & 0xF0)
// mux_fpclass returns an integer that is one of the following:
//
#define MUX_FPCLASS_PINF (MUX_FPGROUP_PINF|0) // Positive infinity (+INF)
#define MUX_FPCLASS_NINF (MUX_FPGROUP_NINF|1) // Negative infinity (-INF)
#define MUX_FPCLASS_QNAN (MUX_FPGROUP_IND |2) // Quiet NAN (Indefinite)
#define MUX_FPCLASS_SNAN (MUX_FPGROUP_NAN |3) // Signaling NAN
#define MUX_FPCLASS_ND (MUX_FPGROUP_ZERO|4) // Negative denormalized
#define MUX_FPCLASS_NZ (MUX_FPGROUP_ZERO|5) // Negative zero (-0)
#define MUX_FPCLASS_PZ (MUX_FPGROUP_ZERO|6) // Positive zero (+0)
#define MUX_FPCLASS_PD (MUX_FPGROUP_ZERO|7) // Positive denormalized
#define MUX_FPCLASS_PN (MUX_FPGROUP_PASS|8) // Positive normalized non-zero
#define MUX_FPCLASS_NN (MUX_FPGROUP_PASS|9) // Negative normalized non-zero
#define MUX_FPCLASS(x) ((x) & 0x0F)
#ifdef WIN32
#define IEEE_MASK_SIGN 0x8000000000000000ui64
#define IEEE_MASK_EXPONENT 0x7FF0000000000000ui64
#define IEEE_MASK_MANTISSA 0x000FFFFFFFFFFFFFui64
#define IEEE_MASK_QNAN 0x0008000000000000ui64
#else
#define IEEE_MASK_SIGN 0x8000000000000000ull
#define IEEE_MASK_EXPONENT 0x7FF0000000000000ull
#define IEEE_MASK_MANTISSA 0x000FFFFFFFFFFFFFull
#define IEEE_MASK_QNAN 0x0008000000000000ull
#endif
#define ARBITRARY_NUMBER 1
#define IEEE_MAKE_TABLESIZE 5
typedef union
{
INT64 i64;
double d;
} SpecialFloatUnion;
// We return a Quiet NAN when a Signalling NAN is requested because
// any operation on a Signalling NAN will result in a Quiet NAN anyway.
// MUX doesn't catch SIGFPE, but if it did, a Signalling NAN would
// generate a SIGFPE.
//
SpecialFloatUnion SpecialFloatTable[IEEE_MAKE_TABLESIZE] =
{
{ 0 }, // Unused.
{ IEEE_MASK_EXPONENT | IEEE_MASK_QNAN | ARBITRARY_NUMBER },
{ IEEE_MASK_EXPONENT | IEEE_MASK_QNAN | ARBITRARY_NUMBER },
{ IEEE_MASK_EXPONENT },
{ IEEE_MASK_EXPONENT | IEEE_MASK_SIGN }
};
double MakeSpecialFloat(int iWhich)
{
return SpecialFloatTable[iWhich].d;
}
static int mux_fpclass(double result)
{
UINT64 i64;
*((double *)&i64) = result;
if ((i64 & IEEE_MASK_EXPONENT) == 0)
{
if (i64 & IEEE_MASK_MANTISSA)
{
if (i64 & IEEE_MASK_SIGN) return MUX_FPCLASS_ND;
else return MUX_FPCLASS_PD;
}
else
{
if (i64 & IEEE_MASK_SIGN) return MUX_FPCLASS_NZ;
else return MUX_FPCLASS_PZ;
}
}
else if ((i64 & IEEE_MASK_EXPONENT) == IEEE_MASK_EXPONENT)
{
if (i64 & IEEE_MASK_MANTISSA)
{
if (i64 & IEEE_MASK_QNAN) return MUX_FPCLASS_QNAN;
else return MUX_FPCLASS_SNAN;
}
else
{
if (i64 & IEEE_MASK_SIGN) return MUX_FPCLASS_NINF;
else return MUX_FPCLASS_PINF;
}
}
else
{
if (i64 & IEEE_MASK_SIGN) return MUX_FPCLASS_NN;
else return MUX_FPCLASS_PN;
}
}
#endif // HAVE_IEEE_FP_FORMAT
static double AddWithError(double& err, double a, double b)
{
double sum = a+b;
err = b-(sum-a);
return sum;
}
// Typically, we are within 1ulp of an exact answer, find the shortest answer
// within that 1 ulp (that is, within 0, +ulp, and -ulp).
//
static double NearestPretty(double R)
{
char *rve = NULL;
int decpt;
int bNegative;
const int mode = 0;
double ulpR = ulp(R);
double R0 = R-ulpR;
double R1 = R+ulpR;
// R.
//
char *p = mux_dtoa(R, mode, 50, &decpt, &bNegative, &rve);
int nDigits = rve - p;
// R-ulp(R)
//
p = mux_dtoa(R0, mode, 50, &decpt, &bNegative, &rve);
if (rve - p < nDigits)
{
nDigits = rve - p;
R = R0;
}
// R+ulp(R)
//
p = mux_dtoa(R1, mode, 50, &decpt, &bNegative, &rve);
if (rve - p < nDigits)
{
nDigits = rve - p;
R = R1;
}
return R;
}
// Compare for decreasing order by absolute value.
//
static int DCL_CDECL f_comp_abs(const void *s1, const void *s2)
{
double a = fabs(*(double *)s1);
double b = fabs(*(double *)s2);
if (a > b)
{
return -1;
}
else if (a < b)
{
return 1;
}
return 0;
}
// Double compensation method. Extended by Priest from Knuth and Kahan.
//
// Error of sum is less than 2*epsilon or 1 ulp except for very large n.
// Return the result that yields the shortest number of base-10 digits.
//
static double AddDoubles(int n, double pd[])
{
qsort(pd, n, sizeof(double), f_comp_abs);
double sum = 0.0;
if (0 < n)
{
sum = pd[0];
double sum_err = 0.0;
int i;
for (i = 1; i < n; i++)
{
double addend_err;
double addend = AddWithError(addend_err, sum_err, pd[i]);
double sum1_err;
double sum1 = AddWithError(sum1_err, sum, addend);
sum = AddWithError(sum_err, sum1, addend_err + sum1_err);
}
}
return NearestPretty(sum);
}
/* ---------------------------------------------------------------------------
* fval: copy the floating point value into a buffer and make it presentable
*/
static void fval(char *buff, char **bufc, double result)
{
// Get double val into buffer.
//
#ifdef HAVE_IEEE_FP_FORMAT
int fpc = mux_fpclass(result);
if (MUX_FPGROUP(fpc) == MUX_FPGROUP_PASS)
{
#endif // HAVE_IEEE_FP_FORMAT
double rIntegerPart;
double rFractionalPart = modf(result, &rIntegerPart);
if ( 0.0 == rFractionalPart
&& LONG_MIN <= rIntegerPart
&& rIntegerPart <= LONG_MAX)
{
long i = (long)rIntegerPart;
safe_ltoa(i, buff, bufc);
}
else
{
safe_str(mux_ftoa(result, false, 0), buff, bufc);
}
#ifdef HAVE_IEEE_FP_FORMAT
}
else
{
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
}
#endif // HAVE_IEEE_FP_FORMAT
}
static const long nMaximums[10] =
{
0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999
};
static double g_aDoubles[(LBUF_SIZE+1)/2];
FUNCTION(fun_add)
{
int i;
for (i = 0; i < nfargs; i++)
{
int nDigits;
long nMaxValue = 0;
if ( !is_integer(fargs[i], &nDigits)
|| nDigits > 9
|| (nMaxValue += nMaximums[nDigits]) > 999999999L)
{
// Do it the slow way.
//
for (int j = 0; j < nfargs; j++)
{
g_aDoubles[j] = mux_atof(fargs[j]);
}
fval(buff, bufc, AddDoubles(nfargs, g_aDoubles));
return;
}
}
// We can do it the fast way.
//
long sum = 0;
for (i = 0; i < nfargs; i++)
{
sum += mux_atol(fargs[i]);
}
safe_ltoa(sum, buff, bufc);
}
FUNCTION(fun_ladd)
{
int n = 0;
if (0 < nfargs)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *cp = trim_space_sep(fargs[0], &sep);
while (cp)
{
char *curr = split_token(&cp, &sep);
g_aDoubles[n++] = mux_atof(curr);
}
}
fval(buff, bufc, AddDoubles(n, g_aDoubles));
}
/////////////////////////////////////////////////////////////////
// Function : iadd(Arg[0], Arg[1],..,Arg[n])
//
// Written by : Chris Rouse (Seraphim) 04/04/2000
/////////////////////////////////////////////////////////////////
FUNCTION(fun_iadd)
{
INT64 sum = 0;
for (int i = 0; i < nfargs; i++)
{
sum += mux_atoi64(fargs[i]);
}
safe_i64toa(sum, buff, bufc);
}
FUNCTION(fun_sub)
{
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
int iResult;
iResult = mux_atol(fargs[0]) - mux_atol(fargs[1]);
safe_ltoa(iResult, buff, bufc);
}
else
{
g_aDoubles[0] = mux_atof(fargs[0]);
g_aDoubles[1] = -mux_atof(fargs[1]);
fval(buff, bufc, AddDoubles(2, g_aDoubles));
}
}
/////////////////////////////////////////////////////////////////
// Function : isub(Arg[0], Arg[1])
//
// Written by : Chris Rouse (Seraphim) 04/04/2000
/////////////////////////////////////////////////////////////////
FUNCTION(fun_isub)
{
INT64 diff = mux_atoi64(fargs[0]) - mux_atoi64(fargs[1]);
safe_i64toa(diff, buff, bufc);
}
FUNCTION(fun_mul)
{
double prod = 1.0;
for (int i = 0; i < nfargs; i++)
{
prod *= mux_atof(fargs[i]);
}
fval(buff, bufc, NearestPretty(prod));
}
/////////////////////////////////////////////////////////////////
// Function : imul(Arg[0], Arg[1], ... , Arg[n])
//
// Written by : Chris Rouse (Seraphim) 04/04/2000
/////////////////////////////////////////////////////////////////
FUNCTION(fun_imul)
{
INT64 prod = 1;
for (int i = 0; i < nfargs; i++)
{
prod *= mux_atoi64(fargs[i]);
}
safe_i64toa(prod, buff, bufc);
}
FUNCTION(fun_gt)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) > mux_atol(fargs[1]));
}
else
{
bResult = (mux_atof(fargs[0]) > mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
FUNCTION(fun_gte)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) >= mux_atol(fargs[1]));
}
else
{
bResult = (mux_atof(fargs[0]) >= mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
FUNCTION(fun_lt)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) < mux_atol(fargs[1]));
}
else
{
bResult = (mux_atof(fargs[0]) < mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
FUNCTION(fun_lte)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) <= mux_atol(fargs[1]));
}
else
{
bResult = (mux_atof(fargs[0]) <= mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
FUNCTION(fun_eq)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) == mux_atol(fargs[1]));
}
else
{
bResult = ( strcmp(fargs[0], fargs[1]) == 0
|| mux_atof(fargs[0]) == mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
FUNCTION(fun_neq)
{
bool bResult = false;
int nDigits;
if ( is_integer(fargs[0], &nDigits)
&& nDigits <= 9
&& is_integer(fargs[1], &nDigits)
&& nDigits <= 9)
{
bResult = (mux_atol(fargs[0]) != mux_atol(fargs[1]));
}
else
{
bResult = ( strcmp(fargs[0], fargs[1]) != 0
&& mux_atof(fargs[0]) != mux_atof(fargs[1]));
}
safe_bool(bResult, buff, bufc);
}
/*
* ---------------------------------------------------------------------------
* * fun_max, fun_min: Return maximum (minimum) value.
*/
FUNCTION(fun_max)
{
double maximum = 0.0;
for (int i = 0; i < nfargs; i++)
{
double tval = mux_atof(fargs[i]);
if ( i == 0
|| tval > maximum)
{
maximum = tval;
}
}
fval(buff, bufc, maximum);
}
FUNCTION(fun_min)
{
double minimum = 0.0;
for (int i = 0; i < nfargs; i++)
{
double tval = mux_atof(fargs[i]);
if ( i == 0
|| tval < minimum)
{
minimum = tval;
}
}
fval(buff, bufc, minimum);
}
/* ---------------------------------------------------------------------------
* fun_sign: Returns -1, 0, or 1 based on the the sign of its argument.
*/
FUNCTION(fun_sign)
{
double num = mux_atof(fargs[0]);
if (num < 0)
{
safe_str("-1", buff, bufc);
}
else
{
safe_bool(num > 0, buff, bufc);
}
}
// fun_isign: Returns -1, 0, or 1 based on the the sign of its argument.
//
FUNCTION(fun_isign)
{
INT64 num = mux_atoi64(fargs[0]);
if (num < 0)
{
safe_str("-1", buff, bufc);
}
else
{
safe_bool(num > 0, buff, bufc);
}
}
// shl() and shr() borrowed from PennMUSH 1.50
//
FUNCTION(fun_shl)
{
if ( is_integer(fargs[0], NULL)
&& is_integer(fargs[1], NULL))
{
safe_i64toa(mux_atoi64(fargs[0]) << mux_atol(fargs[1]), buff, bufc);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
}
}
FUNCTION(fun_shr)
{
if ( is_integer(fargs[0], NULL)
&& is_integer(fargs[1], NULL))
{
safe_i64toa(mux_atoi64(fargs[0]) >> mux_atol(fargs[1]), buff, bufc);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
}
}
FUNCTION(fun_inc)
{
if (nfargs == 1)
{
safe_i64toa(mux_atoi64(fargs[0]) + 1, buff, bufc);
}
else
{
safe_chr('1', buff, bufc);
}
}
FUNCTION(fun_dec)
{
if (nfargs == 1)
{
safe_i64toa(mux_atoi64(fargs[0]) - 1, buff, bufc);
}
else
{
safe_str("-1", buff, bufc);
}
}
FUNCTION(fun_trunc)
{
double rArg = mux_atof(fargs[0]);
double rIntegerPart;
double rFractionalPart;
mux_FPRestore();
rFractionalPart = modf(rArg, &rIntegerPart);
mux_FPSet();
#ifdef HAVE_IEEE_FP_FORMAT
int fpc = mux_fpclass(rIntegerPart);
if (MUX_FPGROUP(fpc) == MUX_FPGROUP_PASS)
{
#endif // HAVE_IEEE_FP_FORMAT
safe_tprintf_str(buff, bufc, "%.0f", rIntegerPart);
#ifdef HAVE_IEEE_FP_FORMAT
}
else
{
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
}
#endif // HAVE_IEEE_FP_FORMAT
}
FUNCTION(fun_fdiv)
{
double bot = mux_atof(fargs[1]);
double top = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if (bot == 0.0)
{
if (top > 0.0)
{
safe_str("+Inf", buff, bufc);
}
else if (top < 0.0)
{
safe_str("-Inf", buff, bufc);
}
else
{
safe_str("Ind", buff, bufc);
}
}
else
{
fval(buff, bufc, top/bot);
}
#else
fval(buff, bufc, top/bot);
#endif
}
FUNCTION(fun_idiv)
{
INT64 bot, top;
bot = mux_atoi64(fargs[1]);
if (bot == 0)
{
safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
}
else
{
top = mux_atoi64(fargs[0]);
top = i64Division(top, bot);
safe_i64toa(top, buff, bufc);
}
}
FUNCTION(fun_floordiv)
{
INT64 bot, top;
bot = mux_atoi64(fargs[1]);
if (bot == 0)
{
safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
}
else
{
top = mux_atoi64(fargs[0]);
top = i64FloorDivision(top, bot);
safe_i64toa(top, buff, bufc);
}
}
FUNCTION(fun_mod)
{
INT64 bot, top;
bot = mux_atoi64(fargs[1]);
if (bot == 0)
{
bot = 1;
}
top = mux_atoi64(fargs[0]);
top = i64Mod(top, bot);
safe_i64toa(top, buff, bufc);
}
FUNCTION(fun_remainder)
{
INT64 bot, top;
bot = mux_atoi64(fargs[1]);
if (bot == 0)
{
bot = 1;
}
top = mux_atoi64(fargs[0]);
top = i64Remainder(top, bot);
safe_i64toa(top, buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_abs: Returns the absolute value of its argument.
*/
FUNCTION(fun_abs)
{
double num = mux_atof(fargs[0]);
if (num == 0.0)
{
safe_chr('0', buff, bufc);
}
else if (num < 0.0)
{
fval(buff, bufc, -num);
}
else
{
fval(buff, bufc, num);
}
}
// fun_iabs: Returns the absolute value of its argument.
//
FUNCTION(fun_iabs)
{
INT64 num = mux_atoi64(fargs[0]);
if (num == 0)
{
safe_chr('0', buff, bufc);
}
else if (num < 0)
{
safe_i64toa(-num, buff, bufc);
}
else
{
safe_i64toa(num, buff, bufc);
}
}
FUNCTION(fun_dist2d)
{
double d;
double sum;
d = mux_atof(fargs[0]) - mux_atof(fargs[2]);
sum = d * d;
d = mux_atof(fargs[1]) - mux_atof(fargs[3]);
sum += d * d;
mux_FPRestore();
double result = sqrt(sum);
mux_FPSet();
fval(buff, bufc, result);
}
FUNCTION(fun_dist3d)
{
double d;
double sum;
d = mux_atof(fargs[0]) - mux_atof(fargs[3]);
sum = d * d;
d = mux_atof(fargs[1]) - mux_atof(fargs[4]);
sum += d * d;
d = mux_atof(fargs[2]) - mux_atof(fargs[5]);
sum += d * d;
mux_FPRestore();
double result = sqrt(sum);
mux_FPSet();
fval(buff, bufc, result);
}
//------------------------------------------------------------------------
// Vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM
// Vectors are space-separated numbers.
//
#define VADD_F 0
#define VSUB_F 1
#define VMUL_F 2
#define VDOT_F 3
#define VCROSS_F 4
static void handle_vectors
(
char *vecarg1, char *vecarg2, char *buff, char **bufc, SEP *psep,
SEP *posep, int flag
)
{
char *v1[(LBUF_SIZE+1)/2], *v2[(LBUF_SIZE+1)/2];
double scalar;
int n, m, i;
// Split the list up, or return if the list is empty.
//
if (!vecarg1 || !*vecarg1 || !vecarg2 || !*vecarg2)
{
return;
}
n = list2arr(v1, (LBUF_SIZE+1)/2, vecarg1, psep);
m = list2arr(v2, (LBUF_SIZE+1)/2, vecarg2, psep);
// vmul() and vadd() accepts a scalar in the first or second arg,
// but everything else has to be same-dimensional.
//
if ( n != m
&& !( ( flag == VMUL_F
|| flag == VADD_F
|| flag == VSUB_F)
&& ( n == 1
|| m == 1)))
{
safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
return;
}
switch (flag)
{
case VADD_F:
// If n or m is 1, this is scalar addition.
// otherwise, add element-wise.
//
if (n == 1)
{
scalar = mux_atof(v1[0]);
for (i = 0; i < m; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v2[i]) + scalar);
}
n = m;
}
else if (m == 1)
{
scalar = mux_atof(v2[0]);
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) + scalar);
}
}
else
{
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) + mux_atof(v2[i]));
}
}
break;
case VSUB_F:
if (n == 1)
{
// This is a scalar minus a vector.
//
scalar = mux_atof(v1[0]);
for (i = 0; i < m; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, scalar - mux_atof(v2[i]));
}
}
else if (m == 1)
{
// This is a vector minus a scalar.
//
scalar = mux_atof(v2[0]);
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) - scalar);
}
}
else
{
// This is a vector minus a vector.
//
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) - mux_atof(v2[i]));
}
}
break;
case VMUL_F:
// If n or m is 1, this is scalar multiplication.
// otherwise, multiply elementwise.
//
if (n == 1)
{
scalar = mux_atof(v1[0]);
for (i = 0; i < m; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v2[i]) * scalar);
}
}
else if (m == 1)
{
scalar = mux_atof(v2[0]);
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) * scalar);
}
}
else
{
// Vector element-wise product.
//
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(posep, buff, bufc);
}
fval(buff, bufc, mux_atof(v1[i]) * mux_atof(v2[i]));
}
}
break;
case VDOT_F:
scalar = 0.0;
for (i = 0; i < n; i++)
{
scalar += mux_atof(v1[i]) * mux_atof(v2[i]);
}
fval(buff, bufc, scalar);
break;
case VCROSS_F:
// cross product: (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd)
//
// Or in other words:
//
// | a b c |
// det | d e f | = i(bf-ce) + j(cd-af) + k(ae-bd)
// | i j k |
//
// where i, j, and k are unit vectors in the x, y, and z
// cartisian coordinate space and are understood when expressed
// in vector form.
//
if (n != 3)
{
safe_str("#-1 VECTORS MUST BE DIMENSION OF 3", buff, bufc);
}
else
{
double a[2][3];
for (i = 0; i < 3; i++)
{
a[0][i] = mux_atof(v1[i]);
a[1][i] = mux_atof(v2[i]);
}
fval(buff, bufc, (a[0][1] * a[1][2]) - (a[0][2] * a[1][1]));
print_sep(posep, buff, bufc);
fval(buff, bufc, (a[0][2] * a[1][0]) - (a[0][0] * a[1][2]));
print_sep(posep, buff, bufc);
fval(buff, bufc, (a[0][0] * a[1][1]) - (a[0][1] * a[1][0]));
}
break;
default:
// If we reached this, we're in trouble.
//
safe_str("#-1 UNIMPLEMENTED", buff, bufc);
}
}
FUNCTION(fun_vadd)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
handle_vectors(fargs[0], fargs[1], buff, bufc, &sep, &osep, VADD_F);
}
FUNCTION(fun_vsub)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
handle_vectors(fargs[0], fargs[1], buff, bufc, &sep, &osep, VSUB_F);
}
FUNCTION(fun_vmul)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
handle_vectors(fargs[0], fargs[1], buff, bufc, &sep, &osep, VMUL_F);
}
FUNCTION(fun_vdot)
{
// dot product: (a,b,c) . (d,e,f) = ad + be + cf
//
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
handle_vectors(fargs[0], fargs[1], buff, bufc, &sep, &osep, VDOT_F);
}
FUNCTION(fun_vcross)
{
// cross product: (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd)
//
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
handle_vectors(fargs[0], fargs[1], buff, bufc, &sep, &osep, VCROSS_F);
}
FUNCTION(fun_vmag)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *v1[LBUF_SIZE];
int n, i;
double tmp, res = 0.0;
// Split the list up, or return if the list is empty.
//
if (!fargs[0] || !*fargs[0])
{
return;
}
n = list2arr(v1, LBUF_SIZE, fargs[0], &sep);
// Calculate the magnitude.
//
for (i = 0; i < n; i++)
{
tmp = mux_atof(v1[i]);
res += tmp * tmp;
}
if (res > 0)
{
mux_FPRestore();
double result = sqrt(res);
mux_FPSet();
fval(buff, bufc, result);
}
else
{
safe_chr('0', buff, bufc);
}
}
FUNCTION(fun_vunit)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *v1[LBUF_SIZE];
int n, i;
double tmp, res = 0.0;
// Split the list up, or return if the list is empty.
//
if (!fargs[0] || !*fargs[0])
{
return;
}
n = list2arr(v1, LBUF_SIZE, fargs[0], &sep);
// Calculate the magnitude.
//
for (i = 0; i < n; i++)
{
tmp = mux_atof(v1[i]);
res += tmp * tmp;
}
if (res <= 0)
{
safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR",
buff, bufc);
return;
}
for (i = 0; i < n; i++)
{
if (i != 0)
{
print_sep(&sep, buff, bufc);
}
mux_FPRestore();
double result = sqrt(res);
mux_FPSet();
fval(buff, bufc, mux_atof(v1[i]) / result);
}
}
FUNCTION(fun_floor)
{
mux_FPRestore();
double r = floor(mux_atof(fargs[0]));
mux_FPSet();
#ifdef HAVE_IEEE_FP_FORMAT
int fpc = mux_fpclass(r);
if (MUX_FPGROUP(fpc) == MUX_FPGROUP_PASS)
{
#endif // HAVE_IEEE_FP_FORMAT
safe_tprintf_str(buff, bufc, "%.0f", r);
#ifdef HAVE_IEEE_FP_FORMAT
}
else
{
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
}
#endif // HAVE_IEEE_FP_FORMAT
}
FUNCTION(fun_ceil)
{
mux_FPRestore();
double r = ceil(mux_atof(fargs[0]));
mux_FPSet();
#ifdef HAVE_IEEE_FP_FORMAT
int fpc = mux_fpclass(r);
if (MUX_FPGROUP(fpc) == MUX_FPGROUP_PASS)
{
#endif // HAVE_IEEE_FP_FORMAT
safe_tprintf_str(buff, bufc, "%.0f", r);
#ifdef HAVE_IEEE_FP_FORMAT
}
else
{
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
}
#endif // HAVE_IEEE_FP_FORMAT
}
FUNCTION(fun_round)
{
double r = mux_atof(fargs[0]);
#ifdef HAVE_IEEE_FP_FORMAT
int fpc = mux_fpclass(r);
if ( MUX_FPGROUP(fpc) == MUX_FPGROUP_PASS
|| MUX_FPGROUP(fpc) == MUX_FPGROUP_ZERO)
{
if (MUX_FPGROUP(fpc) == MUX_FPGROUP_ZERO)
{
r = 0.0;
}
#endif // HAVE_IEEE_FP_FORMAT
int frac = mux_atol(fargs[1]);
safe_str(mux_ftoa(r, true, frac), buff, bufc);
#ifdef HAVE_IEEE_FP_FORMAT
}
else
{
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
}
#endif // HAVE_IEEE_FP_FORMAT
}
FUNCTION(fun_pi)
{
safe_str("3.141592653589793", buff, bufc);
}
FUNCTION(fun_e)
{
safe_str("2.718281828459045", buff, bufc);
}
static double ConvertRDG2R(double d, const char *szUnits)
{
switch (mux_tolower(szUnits[0]))
{
case 'd':
// Degrees to Radians.
//
d *= 0.017453292519943295;
break;
case 'g':
// Gradians to Radians.
//
d *= 0.015707963267948967;
break;
}
return d;
}
static double ConvertR2RDG(double d, const char *szUnits)
{
switch (mux_tolower(szUnits[0]))
{
case 'd':
// Radians to Degrees.
//
d *= 57.29577951308232;
break;
case 'g':
// Radians to Gradians.
//
d *= 63.66197723675813;
break;
}
return d;
}
FUNCTION(fun_ctu)
{
double val = mux_atof(fargs[0]);
val = ConvertRDG2R(val, fargs[1]);
val = ConvertR2RDG(val, fargs[2]);
fval(buff, bufc, val);
}
FUNCTION(fun_sin)
{
double d = mux_atof(fargs[0]);
if (nfargs == 2)
{
d = ConvertRDG2R(d, fargs[1]);
}
mux_FPRestore();
d = sin(d);
mux_FPSet();
fval(buff, bufc, d);
}
FUNCTION(fun_cos)
{
double d = mux_atof(fargs[0]);
if (nfargs == 2)
{
d = ConvertRDG2R(d, fargs[1]);
}
mux_FPRestore();
d = cos(d);
mux_FPSet();
fval(buff, bufc, d);
}
FUNCTION(fun_tan)
{
double d = mux_atof(fargs[0]);
if (nfargs == 2)
{
d = ConvertRDG2R(d, fargs[1]);
}
mux_FPRestore();
d = tan(d);
mux_FPSet();
fval(buff, bufc, d);
}
FUNCTION(fun_asin)
{
double val = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if ((val < -1.0) || (val > 1.0))
{
safe_str("Ind", buff, bufc);
return;
}
#endif
mux_FPRestore();
val = asin(val);
mux_FPSet();
if (nfargs == 2)
{
val = ConvertR2RDG(val, fargs[1]);
}
fval(buff, bufc, val);
}
FUNCTION(fun_acos)
{
double val = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if ((val < -1.0) || (val > 1.0))
{
safe_str("Ind", buff, bufc);
return;
}
#endif
mux_FPRestore();
val = acos(val);
mux_FPSet();
if (nfargs == 2)
{
val = ConvertR2RDG(val, fargs[1]);
}
fval(buff, bufc, val);
}
FUNCTION(fun_atan)
{
double val = mux_atof(fargs[0]);
mux_FPRestore();
val = atan(val);
mux_FPSet();
if (nfargs == 2)
{
val = ConvertR2RDG(val, fargs[1]);
}
fval(buff, bufc, val);
}
FUNCTION(fun_exp)
{
double val = mux_atof(fargs[0]);
mux_FPRestore();
val = exp(val);
mux_FPSet();
fval(buff, bufc, val);
}
FUNCTION(fun_power)
{
double val, val1, val2;
val1 = mux_atof(fargs[0]);
val2 = mux_atof(fargs[1]);
#ifndef HAVE_IEEE_FP_SNAN
if (val1 < 0.0)
{
safe_str("Ind", buff, bufc);
}
else
{
mux_FPRestore();
val = pow(val1, val2);
mux_FPSet();
}
#else
mux_FPRestore();
val = pow(val1, val2);
mux_FPSet();
#endif
fval(buff, bufc, val);
}
FUNCTION(fun_ln)
{
double val;
val = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if (val < 0.0)
{
safe_str("Ind", buff, bufc);
}
else if (val == 0.0)
{
safe_str("-Inf", buff, bufc);
}
else
{
mux_FPRestore();
val = log(val);
mux_FPSet();
}
#else
mux_FPRestore();
val = log(val);
mux_FPSet();
#endif
fval(buff, bufc, val);
}
FUNCTION(fun_log)
{
double val;
val = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if (val < 0.0)
{
safe_str("Ind", buff, bufc);
}
else if (val == 0.0)
{
safe_str("-Inf", buff, bufc);
}
else
{
mux_FPRestore();
val = log10(val);
mux_FPSet();
}
#else
mux_FPRestore();
val = log10(val);
mux_FPSet();
#endif
fval(buff, bufc, val);
}
FUNCTION(fun_sqrt)
{
double val;
val = mux_atof(fargs[0]);
#ifndef HAVE_IEEE_FP_SNAN
if (val < 0.0)
{
safe_str("Ind", buff, bufc);
}
else if (val == 0.0)
{
safe_chr('0', buff, bufc);
}
else
{
mux_FPRestore();
val = sqrt(val);
mux_FPSet();
}
#else
mux_FPRestore();
val = sqrt(val);
mux_FPSet();
#endif
fval(buff, bufc, val);
}
/* ---------------------------------------------------------------------------
* isnum: is the argument a number?
*/
FUNCTION(fun_isnum)
{
safe_bool(is_real(fargs[0]), buff, bufc);
}
/* ---------------------------------------------------------------------------
* israt: is the argument an rational?
*/
FUNCTION(fun_israt)
{
safe_bool(is_rational(fargs[0]), buff, bufc);
}
/* ---------------------------------------------------------------------------
* isint: is the argument an integer?
*/
FUNCTION(fun_isint)
{
safe_bool(is_integer(fargs[0], NULL), buff, bufc);
}
FUNCTION(fun_and)
{
bool val = true;
for (int i = 0; i < nfargs && val; i++)
{
val = isTRUE(mux_atol(fargs[i]));
}
safe_bool(val, buff, bufc);
}
FUNCTION(fun_or)
{
bool val = false;
for (int i = 0; i < nfargs && !val; i++)
{
val = isTRUE(mux_atol(fargs[i]));
}
safe_bool(val, buff, bufc);
}
FUNCTION(fun_andbool)
{
bool val = true;
for (int i = 0; i < nfargs && val; i++)
{
val = xlate(fargs[i]);
}
safe_bool(val, buff, bufc);
}
FUNCTION(fun_orbool)
{
bool val = false;
for (int i = 0; i < nfargs && !val; i++)
{
val = xlate(fargs[i]);
}
safe_bool(val, buff, bufc);
}
FUNCTION(fun_cand)
{
bool val = true;
char *temp = alloc_lbuf("fun_cand");
for (int i = 0; i < nfargs && val && !MuxAlarm.bAlarmed; i++)
{
char *bp = temp;
char *str = fargs[i];
mux_exec(temp, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
val = isTRUE(mux_atol(temp));
}
free_lbuf(temp);
safe_bool(val, buff, bufc);
}
FUNCTION(fun_cor)
{
bool val = false;
char *temp = alloc_lbuf("fun_cor");
for (int i = 0; i < nfargs && !val && !MuxAlarm.bAlarmed; i++)
{
char *bp = temp;
char *str = fargs[i];
mux_exec(temp, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
val = isTRUE(mux_atol(temp));
}
free_lbuf(temp);
safe_bool(val, buff, bufc);
}
FUNCTION(fun_candbool)
{
bool val = true;
char *temp = alloc_lbuf("fun_candbool");
for (int i = 0; i < nfargs && val && !MuxAlarm.bAlarmed; i++)
{
char *bp = temp;
char *str = fargs[i];
mux_exec(temp, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
val = xlate(temp);
}
free_lbuf(temp);
safe_bool(val, buff, bufc);
}
FUNCTION(fun_corbool)
{
bool val = false;
char *temp = alloc_lbuf("fun_corbool");
for (int i = 0; i < nfargs && !val && !MuxAlarm.bAlarmed; i++)
{
char *bp = temp;
char *str = fargs[i];
mux_exec(temp, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
val = xlate(temp);
}
free_lbuf(temp);
safe_bool(val, buff, bufc);
}
FUNCTION(fun_xor)
{
bool val = false;
for (int i = 0; i < nfargs; i++)
{
int tval = mux_atol(fargs[i]);
val = (val && !tval) || (!val && tval);
}
safe_bool(val, buff, bufc);
}
FUNCTION(fun_not)
{
safe_bool(!xlate(fargs[0]), buff, bufc);
}
FUNCTION(fun_t)
{
if ( nfargs <= 0
|| fargs[0][0] == '\0')
{
safe_chr('0', buff, bufc);
}
else
{
safe_bool(xlate(fargs[0]), buff, bufc);
}
}
static const char *bigones[] =
{
"",
"thousand",
"million",
"billion",
"trillion"
};
static const char *singles[] =
{
"",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine"
};
static const char *teens[] =
{
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen"
};
static const char *tens[] =
{
"",
"",
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
};
static const char *th_prefix[] =
{
"",
"ten",
"hundred"
};
class CSpellNum
{
public:
void SpellNum(const char *p, char *buff_arg, char **bufc_arg);
private:
void TwoDigits(const char *p);
void ThreeDigits(const char *p, int iBigOne);
void ManyDigits(int n, const char *p, bool bHundreds);
void FractionalDigits(int n, const char *p);
void StartWord(void);
void AddWord(const char *p);
char *buff;
char **bufc;
bool bNeedSpace;
};
void CSpellNum::StartWord(void)
{
if (bNeedSpace)
{
safe_chr(' ', buff, bufc);
}
bNeedSpace = true;
}
void CSpellNum::AddWord(const char *p)
{
safe_str(p, buff, bufc);
}
// Handle two-character sequences.
//
void CSpellNum::TwoDigits(const char *p)
{
int n0 = p[0] - '0';
int n1 = p[1] - '0';
if (n0 == 0)
{
if (n1 != 0)
{
StartWord();
AddWord(singles[n1]);
}
return;
}
else if (n0 == 1)
{
StartWord();
AddWord(teens[n1]);
return;
}
if (n1 == 0)
{
StartWord();
AddWord(tens[n0]);
}
else
{
StartWord();
AddWord(tens[n0]);
AddWord("-");
AddWord(singles[n1]);
}
}
// Handle three-character sequences.
//
void CSpellNum::ThreeDigits(const char *p, int iBigOne)
{
if ( p[0] == '0'
&& p[1] == '0'
&& p[2] == '0')
{
return;
}
// Handle hundreds.
//
if (p[0] != '0')
{
StartWord();
AddWord(singles[p[0]-'0']);
StartWord();
AddWord("hundred");
}
TwoDigits(p+1);
if (iBigOne > 0)
{
StartWord();
AddWord(bigones[iBigOne]);
}
}
// Handle a series of patterns of three.
//
void CSpellNum::ManyDigits(int n, const char *p, bool bHundreds)
{
// Handle special Hundreds cases.
//
if ( bHundreds
&& n == 4
&& p[1] != '0')
{
TwoDigits(p);
StartWord();
AddWord("hundred");
TwoDigits(p+2);
return;
}
// Handle normal cases.
//
int ndiv = ((n + 2) / 3) - 1;
int nrem = n % 3;
char buf[3];
if (nrem == 0)
{
nrem = 3;
}
int j = nrem;
for (int i = 2; 0 <= i; i--)
{
if (j)
{
j--;
buf[i] = p[j];
}
else
{
buf[i] = '0';
}
}
ThreeDigits(buf, ndiv);
p += nrem;
while (ndiv-- > 0)
{
ThreeDigits(p, ndiv);
p += 3;
}
}
// Handle precision ending for part to the right of the decimal place.
//
void CSpellNum::FractionalDigits(int n, const char *p)
{
ManyDigits(n, p, false);
if ( 0 < n
&& n < 15)
{
int d = n / 3;
int r = n % 3;
StartWord();
if (r != 0)
{
AddWord(th_prefix[r]);
if (d != 0)
{
AddWord("-");
}
}
AddWord(bigones[d]);
AddWord("th");
INT64 i64 = mux_atoi64(p);
if (i64 != 1)
{
AddWord("s");
}
}
}
void CSpellNum::SpellNum(const char *number, char *buff_arg, char **bufc_arg)
{
buff = buff_arg;
bufc = bufc_arg;
bNeedSpace = false;
// Trim Spaces from beginning.
//
while (mux_isspace(*number))
{
number++;
}
if (*number == '-')
{
StartWord();
AddWord("negative");
number++;
}
// Trim Zeroes from Beginning.
//
while (*number == '0')
{
number++;
}
const char *pA = number;
while (mux_isdigit(*number))
{
number++;
}
size_t nA = number - pA;
const char *pB = NULL;
size_t nB = 0;
if (*number == '.')
{
number++;
pB = number;
while (mux_isdigit(*number))
{
number++;
}
nB = number - pB;
}
// Skip trailing spaces.
//
while (mux_isspace(*number))
{
number++;
}
if ( *number
|| nA >= 16
|| nB >= 15)
{
safe_str("#-1 ARGUMENT MUST BE A NUMBER", buff, bufc);
return;
}
if (nA == 0)
{
if (nB == 0)
{
StartWord();
AddWord("zero");
}
}
else
{
ManyDigits(nA, pA, true);
if (nB)
{
StartWord();
AddWord("and");
}
}
if (nB)
{
FractionalDigits(nB, pB);
}
}
FUNCTION(fun_spellnum)
{
CSpellNum sn;
sn.SpellNum(fargs[0], buff, bufc);
}
FUNCTION(fun_roman)
{
const char *number = fargs[0];
// Trim Spaces from beginning.
//
while (mux_isspace(*number))
{
number++;
}
// Trim Zeroes from Beginning.
//
while (*number == '0')
{
number++;
}
const char *pA = number;
while (mux_isdigit(*number))
{
number++;
}
size_t nA = number - pA;
// Skip trailing spaces.
//
while (mux_isspace(*number))
{
number++;
}
// Validate that argument is numeric with a value between 1 and 3999.
//
if (*number || nA < 1)
{
safe_str("#-1 ARGUMENT MUST BE A POSITIVE NUMBER", buff, bufc);
return;
}
else if ( nA > 4
|| ( nA == 1
&& pA[0] == '0')
|| ( nA == 4
&& '3' < pA[0]))
{
safe_range(buff, bufc);
return;
}
// I:1, V:5, X:10, L:50, C:100, D:500, M:1000
//
// Ones: _ I II III IV V VI VII VIII IX
// Tens: _ X XX XXX XL L LX LXX LXXX XC
// Hundreds: _ C CC CCC CD D DC DCC DCCC CM
// Thousands: _ M MM MMM
//
static const char aLetters[4][3] =
{
{ 'I', 'V', 'X' },
{ 'X', 'L', 'C' },
{ 'C', 'D', 'M' },
{ 'M', ' ', ' ' }
};
static const char *aCode[10] =
{
"",
"1",
"11",
"111",
"12",
"2",
"21",
"211",
"2111",
"13"
};
while (nA--)
{
const char *pCode = aCode[*pA - '0'];
const char *pLetters = aLetters[nA];
while (*pCode)
{
safe_chr(pLetters[*pCode - '1'], buff, bufc);
pCode++;
}
pA++;
}
}
/*-------------------------------------------------------------------------
* List-based numeric functions.
*/
FUNCTION(fun_land)
{
bool bValue = true;
if (0 < nfargs)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *cp = trim_space_sep(fargs[0], &sep);
while (cp && bValue)
{
char *curr = split_token(&cp, &sep);
bValue = isTRUE(mux_atol(curr));
}
}
safe_bool(bValue, buff, bufc);
}
FUNCTION(fun_lor)
{
bool bValue = false;
if (0 < nfargs)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *cp = trim_space_sep(fargs[0], &sep);
while (cp && !bValue)
{
char *curr = split_token(&cp, &sep);
bValue = isTRUE(mux_atol(curr));
}
}
safe_bool(bValue, buff, bufc);
}
FUNCTION(fun_band)
{
UINT64 val = UINT64_MAX_VALUE;
for (int i = 0; i < nfargs; i++)
{
if (is_integer(fargs[i], NULL))
{
val &= mux_atoi64(fargs[i]);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
return;
}
}
safe_i64toa(val, buff, bufc);
}
FUNCTION(fun_bor)
{
UINT64 val = 0;
for (int i = 0; i < nfargs; i++)
{
if (is_integer(fargs[i], NULL))
{
val |= mux_atoi64(fargs[i]);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
return;
}
}
safe_i64toa(val, buff, bufc);
}
FUNCTION(fun_bnand)
{
if ( is_integer(fargs[0], NULL)
&& is_integer(fargs[1], NULL))
{
safe_i64toa(mux_atoi64(fargs[0]) & ~(mux_atoi64(fargs[1])), buff, bufc);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
}
}
FUNCTION(fun_bxor)
{
UINT64 val = 0;
for (int i = 0; i < nfargs; i++)
{
if (is_integer(fargs[i], NULL))
{
val ^= mux_atoi64(fargs[i]);
}
else
{
safe_str("#-1 ARGUMENTS MUST BE INTEGERS", buff, bufc);
return;
}
}
safe_i64toa(val, buff, bufc);
}
FUNCTION(fun_crc32)
{
UINT32 ulCRC32 = 0;
for (int i = 0; i < nfargs; i++)
{
int n = strlen(fargs[i]);
ulCRC32 = CRC32_ProcessBuffer(ulCRC32, fargs[i], n);
}
safe_i64toa(ulCRC32, buff, bufc);
}
FUNCTION(fun_sha1)
{
int i;
SHA1_CONTEXT shac;
SHA1_Init(&shac);
for (i = 0; i < nfargs; i++)
{
SHA1_Compute(&shac, strlen(fargs[i]), fargs[i]);
}
SHA1_Final(&shac);
for (i = 0; i <= 4; i++)
{
char buf[9];
sprintf(buf, "%08X", shac.H[i]);
safe_str(buf, buff, bufc);
}
}