mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
The flip: FUNCTION/XFUNCTION/FUN::fun/delim_check and the module interfaces take `const UTF8 * const fargs[]`. Double-const is load-bearing: C++ qualification conversion needs const at both pointer levels, so builder-side `UTF8 *[]` arrays convert implicitly — the evaluator, the JIT marshaller, and every owner site need zero casts, and slot reassignment inside bodies becomes a compile error for free. The conversions: the flip landed first so the compiler enumerated every violation; this commit is that inventory worked to zero — ~250 sites across funceval, funceval2, functions, funmath, help, mail, session, powers, levels, predicates, conf, walkdb, stringutil, timeutil/ date_scan (regenerated, one-line diff), exp3, and mux_main, each classified per docs/campaign-2136-const-fargs.md's four recipes. New idioms (functions.h): trim_space_sep_n() — non-destructive trim for (pointer, length) consumers, so trim-then-scan sites need no copy at all; FargVec — the argv counterpart of FargCopy for CS_ARGV handlers. countwords() and DecodeListOfIntegers() rewritten non-destructive. The flip deleted more than it added: #2157's fun_munge list1 copy, the engine_com help-topic copy, fun_index's in-place NUL write, and five const_casts (process_sex x4, sha1_helper). const_cast budget: zero added. Trap recorded in the brief: an old-signature definition doesn't fail the build — it becomes a C++ overload, and the new-signature symbol stays undefined until dlopen(RTLD_NOW). delim_check, the conn_bridge bridges, the dbt_spike stub, and exp3::Call were all silently shadowed; muxscript was the only host that noticed, because netmux's own net.cpp resolved the flat-namespace lookup. After any signature flip, grep the old spelling. Verified: make test EXPECT_CONFIG="jit=yes" (35 passed / 0 failed) and make test-scenario, including the new tests/scenario/sidefx_fargs.py that live-probes the class-3 wrappers smoke never touches (pemit/ trigger/link/tel/wipe/destroy). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3636 lines
82 KiB
C++
3636 lines
82 KiB
C++
/*! \file funmath.cpp
|
|
* \brief MUX math function handlers.
|
|
*
|
|
*/
|
|
|
|
#include "copyright.h"
|
|
#include "autoconf.h"
|
|
#include "config.h"
|
|
#include "externs.h"
|
|
#include "sha1.h"
|
|
|
|
#include <algorithm>
|
|
#include <climits>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
static const long nMaximums[10] =
|
|
{
|
|
0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999
|
|
};
|
|
|
|
static double g_aDoubles[MAX_WORDS];
|
|
|
|
FUNCTION(fun_add)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int nArgs = nfargs;
|
|
if (MAX_WORDS < nArgs)
|
|
{
|
|
nArgs = MAX_WORDS;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < nArgs; 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 < nArgs; j++)
|
|
{
|
|
g_aDoubles[j] = mux_atof(fargs[j]);
|
|
}
|
|
|
|
fval(buff, bufc, AddDoubles(nArgs, g_aDoubles));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We can do it the fast way.
|
|
//
|
|
int64_t sum = 0;
|
|
for (i = 0; i < nArgs; i++)
|
|
{
|
|
sum += mux_atoi64(fargs[i]);
|
|
}
|
|
safe_i64toa(sum, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_ladd)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int n = 0;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_ladd.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while ( cp
|
|
&& n < MAX_WORDS)
|
|
{
|
|
UTF8 *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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// #1861: signed overflow is UB; wrap via i64Add (see #1472 / timeutil.h).
|
|
//
|
|
int64_t sum = 0;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
sum = i64Add(sum, mux_atoi64(fargs[i]));
|
|
}
|
|
safe_i64toa(sum, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_sub)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
safe_i64toa(a - b, 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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// #1861: wrap via i64Sub rather than signed a - b.
|
|
//
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
safe_i64toa(i64Sub(a, b), buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_mul)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// #1861: wrap via i64Mul rather than signed *=.
|
|
//
|
|
int64_t prod = 1;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
prod = i64Mul(prod, mux_atoi64(fargs[i]));
|
|
}
|
|
safe_i64toa(prod, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_gt)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = false;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a > b);
|
|
}
|
|
else
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a > b);
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_gte)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = false;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a >= b);
|
|
}
|
|
else
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a >= b);
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lt)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = false;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a < b);
|
|
}
|
|
else
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a < b);
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lte)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = false;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a <= b);
|
|
}
|
|
else
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a <= b);
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_eq)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = true;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a == b);
|
|
}
|
|
else
|
|
{
|
|
if (strcmp(reinterpret_cast<const char *>(fargs[0]), reinterpret_cast<const char *>(fargs[1])) != 0)
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a == b);
|
|
}
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_neq)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bResult = false;
|
|
int nDigits;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& nDigits <= 9
|
|
&& is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 9)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
bResult = (a != b);
|
|
}
|
|
else
|
|
{
|
|
if (strcmp(reinterpret_cast<const char *>(fargs[0]), reinterpret_cast<const char *>(fargs[1])) != 0)
|
|
{
|
|
double a = mux_atof(fargs[0]);
|
|
double b = mux_atof(fargs[1]);
|
|
bResult = (a != b);
|
|
}
|
|
}
|
|
safe_bool(bResult, buff, bufc);
|
|
}
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* * fun_max, fun_min: Return maximum (minimum) value.
|
|
*/
|
|
|
|
FUNCTION(fun_max)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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_lmax)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double maximum = 0.0;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int n = 0;
|
|
LBuf scList = LBuf_Src("fun_lmax.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (nullptr != cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
double tval = mux_atof(curr);
|
|
if ( n++ == 0
|
|
|| tval > maximum)
|
|
{
|
|
maximum = tval;
|
|
}
|
|
}
|
|
}
|
|
fval(buff, bufc, maximum);
|
|
}
|
|
|
|
FUNCTION(fun_min)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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);
|
|
}
|
|
|
|
FUNCTION(fun_lmin)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double minimum = 0.0;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int n = 0;
|
|
LBuf scList = LBuf_Src("fun_lmin.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (nullptr != cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
double tval = mux_atof(curr);
|
|
if ( n++ == 0
|
|
|| tval < minimum)
|
|
{
|
|
minimum = tval;
|
|
}
|
|
}
|
|
}
|
|
fval(buff, bufc, minimum);
|
|
}
|
|
|
|
FUNCTION(fun_lmath)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// lmath(<operation>, <list>[, <delim>])
|
|
//
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int n = 0;
|
|
LBuf scList = LBuf_Src("fun_lmath.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[1]), sep);
|
|
while ( cp
|
|
&& n < MAX_WORDS)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
g_aDoubles[n++] = mux_atof(curr);
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
safe_chr('0', buff, bufc);
|
|
return;
|
|
}
|
|
|
|
const UTF8 *op = fargs[0];
|
|
|
|
if ( mux_stricmp(op, T("add")) == 0
|
|
|| mux_stricmp(op, T("sum")) == 0)
|
|
{
|
|
fval(buff, bufc, AddDoubles(n, g_aDoubles));
|
|
}
|
|
else if (mux_stricmp(op, T("mul")) == 0)
|
|
{
|
|
double prod = 1.0;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
prod *= g_aDoubles[i];
|
|
}
|
|
fval(buff, bufc, NearestPretty(prod));
|
|
}
|
|
else if (mux_stricmp(op, T("sub")) == 0)
|
|
{
|
|
double result = g_aDoubles[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
result -= g_aDoubles[i];
|
|
}
|
|
fval(buff, bufc, NearestPretty(result));
|
|
}
|
|
else if (mux_stricmp(op, T("div")) == 0)
|
|
{
|
|
double result = g_aDoubles[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (g_aDoubles[i] == 0.0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
return;
|
|
}
|
|
result /= g_aDoubles[i];
|
|
}
|
|
fval(buff, bufc, NearestPretty(result));
|
|
}
|
|
else if (mux_stricmp(op, T("mod")) == 0)
|
|
{
|
|
int64_t result = static_cast<int64_t>(g_aDoubles[0]);
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
int64_t divisor = static_cast<int64_t>(g_aDoubles[i]);
|
|
if (divisor == 0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
return;
|
|
}
|
|
result = i64Mod(result, divisor);
|
|
}
|
|
safe_i64toa(result, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("min")) == 0)
|
|
{
|
|
double minimum = g_aDoubles[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (g_aDoubles[i] < minimum)
|
|
{
|
|
minimum = g_aDoubles[i];
|
|
}
|
|
}
|
|
fval(buff, bufc, minimum);
|
|
}
|
|
else if (mux_stricmp(op, T("max")) == 0)
|
|
{
|
|
double maximum = g_aDoubles[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (g_aDoubles[i] > maximum)
|
|
{
|
|
maximum = g_aDoubles[i];
|
|
}
|
|
}
|
|
fval(buff, bufc, maximum);
|
|
}
|
|
else if ( mux_stricmp(op, T("mean")) == 0
|
|
|| mux_stricmp(op, T("avg")) == 0)
|
|
{
|
|
fval(buff, bufc, AddDoubles(n, g_aDoubles) / n);
|
|
}
|
|
else if (mux_stricmp(op, T("median")) == 0)
|
|
{
|
|
// #1119: O(n log n) sort — insertion sort was O(n²) up to MAX_WORDS.
|
|
//
|
|
if (alarm_clock.alarmed)
|
|
{
|
|
safe_str(S_("#-1 CPU LIMITED"), buff, bufc);
|
|
return;
|
|
}
|
|
std::sort(g_aDoubles, g_aDoubles + n);
|
|
if (n % 2 == 1)
|
|
{
|
|
fval(buff, bufc, g_aDoubles[n / 2]);
|
|
}
|
|
else
|
|
{
|
|
g_aDoubles[0] = g_aDoubles[n / 2 - 1];
|
|
g_aDoubles[1] = g_aDoubles[n / 2];
|
|
fval(buff, bufc, AddDoubles(2, g_aDoubles) / 2.0);
|
|
}
|
|
}
|
|
else if (mux_stricmp(op, T("stddev")) == 0)
|
|
{
|
|
double mean = AddDoubles(n, g_aDoubles) / n;
|
|
double sumSqDiff = 0.0;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
double diff = g_aDoubles[i] - mean;
|
|
sumSqDiff += diff * diff;
|
|
}
|
|
fval(buff, bufc, sqrt(sumSqDiff / n));
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 UNKNOWN OPERATION"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// limath: Integer-only list reduction, parallel to lmath().
|
|
//
|
|
// limath(<operation>, <list>[, <delim>])
|
|
//
|
|
// Supported operations: add/sum, sub, mul, div, mod, min, max, median.
|
|
// All arithmetic is 64-bit integer.
|
|
//
|
|
FUNCTION(fun_limath)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// #1110: heap-allocate — vals[MAX_WORDS] was ~256 KiB on the stack and
|
|
// ate recursion margin under deep mux_exec (leaf, but still one big frame).
|
|
//
|
|
std::vector<int64_t> vals;
|
|
vals.reserve(64);
|
|
LBuf scList = LBuf_Src("fun_limath.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[1]), sep);
|
|
while (cp)
|
|
{
|
|
if (static_cast<int>(vals.size()) >= MAX_WORDS)
|
|
{
|
|
safe_str(S_("#-1 LIST TOO LONG"), buff, bufc);
|
|
return;
|
|
}
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
if (!is_integer(curr, nullptr))
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
vals.push_back(mux_atoi64(curr));
|
|
}
|
|
|
|
const int n = static_cast<int>(vals.size());
|
|
if (n == 0)
|
|
{
|
|
safe_chr('0', buff, bufc);
|
|
return;
|
|
}
|
|
|
|
const UTF8 *op = fargs[0];
|
|
|
|
if ( mux_stricmp(op, T("add")) == 0
|
|
|| mux_stricmp(op, T("sum")) == 0)
|
|
{
|
|
// #1861: wrap via i64Add (see timeutil.h / #1472).
|
|
//
|
|
int64_t sum = 0;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
sum = i64Add(sum, vals[i]);
|
|
}
|
|
safe_i64toa(sum, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("mul")) == 0)
|
|
{
|
|
int64_t prod = 1;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
prod = i64Mul(prod, vals[i]);
|
|
}
|
|
safe_i64toa(prod, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("sub")) == 0)
|
|
{
|
|
int64_t result = vals[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
result = i64Sub(result, vals[i]);
|
|
}
|
|
safe_i64toa(result, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("div")) == 0)
|
|
{
|
|
int64_t result = vals[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (vals[i] == 0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
return;
|
|
}
|
|
result = i64Division(result, vals[i]);
|
|
}
|
|
safe_i64toa(result, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("mod")) == 0)
|
|
{
|
|
int64_t result = vals[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (vals[i] == 0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
return;
|
|
}
|
|
result = i64Mod(result, vals[i]);
|
|
}
|
|
safe_i64toa(result, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("min")) == 0)
|
|
{
|
|
int64_t minimum = vals[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (vals[i] < minimum)
|
|
{
|
|
minimum = vals[i];
|
|
}
|
|
}
|
|
safe_i64toa(minimum, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("max")) == 0)
|
|
{
|
|
int64_t maximum = vals[0];
|
|
for (int i = 1; i < n; i++)
|
|
{
|
|
if (vals[i] > maximum)
|
|
{
|
|
maximum = vals[i];
|
|
}
|
|
}
|
|
safe_i64toa(maximum, buff, bufc);
|
|
}
|
|
else if (mux_stricmp(op, T("median")) == 0)
|
|
{
|
|
// #1119: O(n log n) sort — insertion sort was O(n²) up to MAX_WORDS.
|
|
//
|
|
if (alarm_clock.alarmed)
|
|
{
|
|
safe_str(S_("#-1 CPU LIMITED"), buff, bufc);
|
|
return;
|
|
}
|
|
std::sort(vals.begin(), vals.end());
|
|
if (n % 2 == 1)
|
|
{
|
|
safe_i64toa(vals[n / 2], buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
// Integer median of even-length list: floor of average.
|
|
// a + (b - a) / 2 with defined wrap (#1861) so INT64_MIN/MAX
|
|
// pairs cannot invoke signed overflow.
|
|
//
|
|
int64_t a = vals[n / 2 - 1];
|
|
int64_t b = vals[n / 2];
|
|
safe_i64toa(i64Add(a, i64Division(i64Sub(b, a), 2)), buff, bufc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 UNKNOWN OPERATION"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* fun_sign: Returns -1, 0, or 1 based on the the sign of its argument.
|
|
*/
|
|
|
|
FUNCTION(fun_sign)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double num = mux_atof(fargs[0]);
|
|
if (num < 0)
|
|
{
|
|
safe_str(T("-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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t num = mux_atoi64(fargs[0]);
|
|
|
|
if (num < 0)
|
|
{
|
|
safe_str(T("-1"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_bool(num > 0, buff, bufc);
|
|
}
|
|
}
|
|
|
|
// shl() and shr() borrowed from PennMUSH 1.50
|
|
//
|
|
FUNCTION(fun_shl)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if ( is_integer(fargs[0], nullptr)
|
|
&& is_integer(fargs[1], nullptr))
|
|
{
|
|
// #1109: shift count must be in [0, 63] for int64_t — larger is UB.
|
|
//
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
if (0 <= b && b < 64)
|
|
{
|
|
// #1472: bounding the count is necessary but not sufficient.
|
|
// Left-shifting a negative value is undefined however small the
|
|
// count, and so is shifting a bit into or past the sign bit --
|
|
// shl(-2,1), shl(1,63) and shl(3,63) all reach it. Shift in
|
|
// uint64_t, where the operation is defined as the modular one
|
|
// this function already documents, and convert back. Every
|
|
// result is bit-for-bit what the wrap produced before.
|
|
//
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
uint64_t ua = static_cast<uint64_t>(a) << b;
|
|
safe_i64toa(static_cast<int64_t>(ua), buff, bufc);
|
|
}
|
|
else if (b < 0)
|
|
{
|
|
// Keep historical wording for smoke tests (0 is allowed).
|
|
safe_str(S_("#-1 SECOND ARGUMENT MUST BE A POSITIVE NUMBER"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 SECOND ARGUMENT MUST BE LESS THAN 64"), buff, bufc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_shr)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if ( is_integer(fargs[0], nullptr)
|
|
&& is_integer(fargs[1], nullptr))
|
|
{
|
|
// #1109: shift count must be in [0, 63] for int64_t — larger is UB.
|
|
//
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
if (0 <= b && b < 64)
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
safe_i64toa(a >> b, buff, bufc);
|
|
}
|
|
else if (b < 0)
|
|
{
|
|
safe_str(S_("#-1 SECOND ARGUMENT MUST BE A POSITIVE NUMBER"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 SECOND ARGUMENT MUST BE LESS THAN 64"), buff, bufc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_inc)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if (nfargs == 1)
|
|
{
|
|
// #1472: wraps at INT64_MAX, which is signed overflow. Add in
|
|
// uint64_t for the same wrap without the undefined behaviour.
|
|
//
|
|
uint64_t v = static_cast<uint64_t>(mux_atoi64(fargs[0]));
|
|
safe_i64toa(static_cast<int64_t>(v + 1), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_chr('1', buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_dec)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if (nfargs == 1)
|
|
{
|
|
// #1472: wraps at INT64_MIN, which is signed overflow. Subtract in
|
|
// uint64_t for the same wrap without the undefined behaviour.
|
|
//
|
|
uint64_t v = static_cast<uint64_t>(mux_atoi64(fargs[0]));
|
|
safe_i64toa(static_cast<int64_t>(v - 1), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(T("-1"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_trunc)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double rArg = mux_atof(fargs[0]);
|
|
double rIntegerPart;
|
|
|
|
mux_FPRestore();
|
|
(void)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
|
|
fval(buff, bufc, rIntegerPart);
|
|
#ifdef HAVE_IEEE_FP_FORMAT
|
|
}
|
|
else
|
|
{
|
|
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
|
|
}
|
|
#endif // HAVE_IEEE_FP_FORMAT
|
|
}
|
|
|
|
FUNCTION(fun_fdiv)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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(T("+Inf"), buff, bufc);
|
|
}
|
|
else if (top < 0.0)
|
|
{
|
|
safe_str(T("-Inf"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(T("Ind"), buff, bufc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fval(buff, bufc, top/bot);
|
|
}
|
|
#else
|
|
fval(buff, bufc, top/bot);
|
|
#endif
|
|
}
|
|
|
|
FUNCTION(fun_idiv)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t bot, top;
|
|
|
|
bot = mux_atoi64(fargs[1]);
|
|
if (bot == 0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
top = mux_atoi64(fargs[0]);
|
|
top = i64Division(top, bot);
|
|
safe_i64toa(top, buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_floordiv)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t bot, top;
|
|
|
|
bot = mux_atoi64(fargs[1]);
|
|
if (bot == 0)
|
|
{
|
|
safe_str(S_("#-1 DIVIDE BY ZERO"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
top = mux_atoi64(fargs[0]);
|
|
top = i64FloorDivision(top, bot);
|
|
safe_i64toa(top, buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_mod)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t 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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t 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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
// #1255: |INT64_MIN| is 2**63. mux_atof can take the literal, but
|
|
// fval formats whole numbers near that magnitude as int64 and wraps
|
|
// back to a negative string — abs() returning a negative. Reject
|
|
// the exact integer domain like iabs() (#1114): error beats silent
|
|
// nonsense. Non-integer strings still take the float path.
|
|
//
|
|
int nDigits = 0;
|
|
if ( is_integer(fargs[0], &nDigits)
|
|
&& 0 < nDigits
|
|
&& mux_atoi64(fargs[0]) == INT64_MIN)
|
|
{
|
|
safe_range(buff, bufc);
|
|
return;
|
|
}
|
|
|
|
double num = mux_atof(fargs[0]);
|
|
if (0.0 == num)
|
|
{
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
int64_t num = mux_atoi64(fargs[0]);
|
|
|
|
if (num == 0)
|
|
{
|
|
safe_chr('0', buff, bufc);
|
|
}
|
|
else if (num == INT64_MIN)
|
|
{
|
|
// #1114: |INT64_MIN| is 2**63, which int64_t cannot represent —
|
|
// negating it is UB. Reject rather than hand back the magnitude
|
|
// as a string: "9223372036854775808" is not a valid int64, and
|
|
// every consumer corrupts it. Measured on this tree, feeding it
|
|
// to an integer-path function re-parses through mux_atoi64 (which
|
|
// wraps rather than saturates) straight back to INT64_MIN —
|
|
// idiv(iabs(-9223372036854775808),1) and shl(...,0) both yield
|
|
// -9223372036854775808 — while the float path loses precision
|
|
// instead (add(...,0) -> 9223372036854769664). Either way iabs()'s
|
|
// one invariant is silently broken. Failing loudly matches how the
|
|
// integer family handles an out-of-domain argument (fun_table,
|
|
// fun_columns).
|
|
//
|
|
safe_range(buff, bufc);
|
|
}
|
|
else if (num < 0)
|
|
{
|
|
safe_i64toa(-num, buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_i64toa(num, buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_dist2d)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double a, b, d;
|
|
double sum;
|
|
|
|
a = mux_atof(fargs[0]);
|
|
b = mux_atof(fargs[2]);
|
|
d = a - b;
|
|
sum = d * d;
|
|
a = mux_atof(fargs[1]);
|
|
b = mux_atof(fargs[3]);
|
|
d = a - b;
|
|
sum += d * d;
|
|
|
|
mux_FPRestore();
|
|
double result = sqrt(sum);
|
|
mux_FPSet();
|
|
|
|
fval(buff, bufc, result);
|
|
}
|
|
|
|
FUNCTION(fun_dist3d)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double a, b, d;
|
|
double sum;
|
|
|
|
a = mux_atof(fargs[0]);
|
|
b = mux_atof(fargs[3]);
|
|
d = a - b;
|
|
sum = d * d;
|
|
a = mux_atof(fargs[1]);
|
|
b = mux_atof(fargs[4]);
|
|
d = a - b;
|
|
sum += d * d;
|
|
a = mux_atof(fargs[2]);
|
|
b = mux_atof(fargs[5]);
|
|
d = a - b;
|
|
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
|
|
(
|
|
const UTF8 *vecarg1, const UTF8 *vecarg2, UTF8 *buff, UTF8 **bufc,
|
|
const SEP &sep, const SEP &osep, int flag
|
|
)
|
|
{
|
|
// Return if the list is empty.
|
|
//
|
|
if (!vecarg1 || !*vecarg1 || !vecarg2 || !*vecarg2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Uninitialized on purpose (#2145): list2arr writes arr[i] only for
|
|
// i < its return value and nothing reads past it, so value-initializing
|
|
// 2x 128 KB of pointers per call bought nothing — and was ~170x the
|
|
// cost of actually splitting a small vector.
|
|
//
|
|
std::unique_ptr<UTF8*[]> v1(new UTF8*[(LBUF_SIZE+1)/2]);
|
|
std::unique_ptr<UTF8*[]> v2(new UTF8*[(LBUF_SIZE+1)/2]);
|
|
|
|
// Split the lists up, or return if a list is empty. Non-destructive
|
|
// (#2136): vecarg1/vecarg2 are the caller's fargs, borrowed memory.
|
|
//
|
|
LBuf sc1 = LBuf_Src("handle_vectors.1");
|
|
LBuf sc2 = LBuf_Src("handle_vectors.2");
|
|
int n = list2arr_nd(v1.get(), (LBUF_SIZE+1)/2, vecarg1, sep, sc1);
|
|
int m = list2arr_nd(v2.get(), (LBUF_SIZE+1)/2, vecarg2, sep, sc2);
|
|
|
|
// 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(S_("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bufc);
|
|
return;
|
|
}
|
|
|
|
double scalar;
|
|
int i;
|
|
|
|
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(osep, 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(osep, buff, bufc);
|
|
}
|
|
fval(buff, bufc, mux_atof(v1[i]) + scalar);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (i != 0)
|
|
{
|
|
print_sep(osep, buff, bufc);
|
|
}
|
|
double a = mux_atof(v1[i]);
|
|
double b = mux_atof(v2[i]);
|
|
fval(buff, bufc, a + b);
|
|
}
|
|
}
|
|
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(osep, 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(osep, 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(osep, buff, bufc);
|
|
}
|
|
double a = mux_atof(v1[i]);
|
|
double b = mux_atof(v2[i]);
|
|
fval(buff, bufc, a - b);
|
|
}
|
|
}
|
|
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(osep, 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(osep, 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(osep, buff, bufc);
|
|
}
|
|
double a = mux_atof(v1[i]);
|
|
double b = mux_atof(v2[i]);
|
|
fval(buff, bufc, a * b);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VDOT_F:
|
|
|
|
scalar = 0.0;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
double a = mux_atof(v1[i]);
|
|
double b = mux_atof(v2[i]);
|
|
scalar += a * b;
|
|
}
|
|
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(S_("#-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(osep, buff, bufc);
|
|
fval(buff, bufc, (a[0][2] * a[1][0]) - (a[0][0] * a[1][2]));
|
|
print_sep(osep, 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(S_("#-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;
|
|
}
|
|
|
|
// Split the list up, or return if the list is empty.
|
|
//
|
|
if (!fargs[0] || !*fargs[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<UTF8*[]> v1(new UTF8*[LBUF_SIZE/2]); // uninit (#2145): past-count reads are UB now, not nullptr
|
|
LBuf sc = LBuf_Src("fun_vmag.nd");
|
|
int n = list2arr_nd(v1.get(), LBUF_SIZE/2, fargs[0], sep, sc);
|
|
|
|
// Calculate the magnitude.
|
|
//
|
|
double res = 0.0;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
double 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;
|
|
}
|
|
|
|
// Split the list up, or return if the list is empty.
|
|
//
|
|
if (!fargs[0] || !*fargs[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<UTF8*[]> v1(new UTF8*[LBUF_SIZE/2]); // uninit (#2145): past-count reads are UB now, not nullptr
|
|
LBuf sc = LBuf_Src("fun_vunit.nd");
|
|
int n = list2arr_nd(v1.get(), LBUF_SIZE/2, fargs[0], sep, sc);
|
|
|
|
// Calculate the magnitude.
|
|
//
|
|
int i;
|
|
double res = 0.0;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
double tmp = mux_atof(v1[i]);
|
|
res += tmp * tmp;
|
|
}
|
|
|
|
if (res <= 0)
|
|
{
|
|
safe_str(S_("#-1 CANNOT MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR"),
|
|
buff, bufc);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
if (0 != i)
|
|
{
|
|
print_sep(sep, buff, bufc);
|
|
}
|
|
|
|
mux_FPRestore();
|
|
double result = sqrt(res);
|
|
mux_FPSet();
|
|
|
|
fval(buff, bufc, mux_atof(v1[i]) / result);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_floor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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
|
|
fval(buff, bufc, r);
|
|
#ifdef HAVE_IEEE_FP_FORMAT
|
|
}
|
|
else
|
|
{
|
|
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
|
|
}
|
|
#endif // HAVE_IEEE_FP_FORMAT
|
|
}
|
|
|
|
FUNCTION(fun_ceil)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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
|
|
fval(buff, bufc, r);
|
|
#ifdef HAVE_IEEE_FP_FORMAT
|
|
}
|
|
else
|
|
{
|
|
safe_str(mux_FPStrings[MUX_FPCLASS(fpc)], buff, bufc);
|
|
}
|
|
#endif // HAVE_IEEE_FP_FORMAT
|
|
}
|
|
|
|
FUNCTION(fun_round)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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
|
|
int64_t frac = mux_atoi64(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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(fargs);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_str(T("3.141592653589793"), buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_e)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(fargs);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_str(T("2.718281828459045"), buff, bufc);
|
|
}
|
|
|
|
static double ConvertRDG2R(double d, const UTF8 *szUnits)
|
|
{
|
|
switch (szUnits[0])
|
|
{
|
|
case 'd':
|
|
case 'D':
|
|
// Degrees to Radians.
|
|
//
|
|
d *= 0.017453292519943295;
|
|
break;
|
|
|
|
case 'g':
|
|
case 'G':
|
|
// Gradians to Radians.
|
|
//
|
|
d *= 0.015707963267948967;
|
|
break;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
static double ConvertR2RDG(double d, const UTF8 *szUnits)
|
|
{
|
|
switch (szUnits[0])
|
|
{
|
|
case 'd':
|
|
case 'D':
|
|
// Radians to Degrees.
|
|
//
|
|
d *= 57.29577951308232;
|
|
break;
|
|
|
|
case 'g':
|
|
case 'G':
|
|
// Radians to Gradians.
|
|
//
|
|
d *= 63.66197723675813;
|
|
break;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
FUNCTION(fun_ctu)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val = mux_atof(fargs[0]);
|
|
val = ConvertRDG2R(val, fargs[1]);
|
|
val = ConvertR2RDG(val, fargs[2]);
|
|
fval(buff, bufc, val);
|
|
}
|
|
|
|
FUNCTION(fun_sin)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val = mux_atof(fargs[0]);
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
if ((val < -1.0) || (val > 1.0))
|
|
{
|
|
safe_str(T("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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val = mux_atof(fargs[0]);
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
if ((val < -1.0) || (val > 1.0))
|
|
{
|
|
safe_str(T("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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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_atan2)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val1 = mux_atof(fargs[0]);
|
|
double val2 = mux_atof(fargs[1]);
|
|
|
|
mux_FPRestore();
|
|
val1 = atan2(val1, val2);
|
|
mux_FPSet();
|
|
|
|
if (3 == nfargs)
|
|
{
|
|
val1 = ConvertR2RDG(val1, fargs[2]);
|
|
}
|
|
fval(buff, bufc, val1);
|
|
}
|
|
|
|
FUNCTION(fun_exp)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val = mux_atof(fargs[0]);
|
|
|
|
mux_FPRestore();
|
|
val = exp(val);
|
|
mux_FPSet();
|
|
|
|
fval(buff, bufc, val);
|
|
}
|
|
|
|
FUNCTION(fun_power)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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(T("Ind"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
mux_FPRestore();
|
|
val = pow(val1, val2);
|
|
mux_FPSet();
|
|
fval(buff, bufc, val);
|
|
}
|
|
#else
|
|
mux_FPRestore();
|
|
val = pow(val1, val2);
|
|
mux_FPSet();
|
|
fval(buff, bufc, val);
|
|
#endif
|
|
}
|
|
|
|
FUNCTION(fun_fmod)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val, val1, val2;
|
|
|
|
val1 = mux_atof(fargs[0]);
|
|
val2 = mux_atof(fargs[1]);
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
// The indeterminate case is a zero *divisor* (fmod(x,0) is NaN);
|
|
// fmod(0,y) is a valid 0. The guard checked val1 (the dividend),
|
|
// copy-pasted from fun_power's val1 sign check — so on a
|
|
// NO_IEEE_FP_SNAN lane fmod(0,3) wrongly returned Ind and fmod(x,0)
|
|
// fell through unguarded. Reported by ThresholdOps.
|
|
if (val2 == 0.0)
|
|
{
|
|
safe_str(T("Ind"), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
mux_FPRestore();
|
|
val = fmod(val1, val2);
|
|
mux_FPSet();
|
|
fval(buff, bufc, val);
|
|
}
|
|
#else
|
|
mux_FPRestore();
|
|
val = fmod(val1, val2);
|
|
mux_FPSet();
|
|
fval(buff, bufc, val);
|
|
#endif
|
|
}
|
|
|
|
FUNCTION(fun_ln)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val;
|
|
|
|
val = mux_atof(fargs[0]);
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
if (val < 0.0)
|
|
{
|
|
safe_str(T("Ind"), buff, bufc);
|
|
}
|
|
else if (val == 0.0)
|
|
{
|
|
safe_str(T("-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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
typedef enum
|
|
{
|
|
#ifdef HAVE_LOG2
|
|
kBinary,
|
|
#endif
|
|
kNatural,
|
|
kCommon,
|
|
kOther
|
|
} logarithm_base;
|
|
|
|
logarithm_base kBase;
|
|
|
|
double val = mux_atof(fargs[0]);
|
|
|
|
double base;
|
|
if (2 == nfargs)
|
|
{
|
|
int nDigits;
|
|
if ( is_integer(fargs[1], &nDigits)
|
|
&& nDigits <= 2)
|
|
{
|
|
int64_t iBase = mux_atoi64(fargs[1]);
|
|
if (10 == iBase)
|
|
{
|
|
kBase = kCommon;
|
|
}
|
|
#ifdef HAVE_LOG2
|
|
else if (2 == iBase)
|
|
{
|
|
kBase = kBinary;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
kBase = kOther;
|
|
base = mux_atof(fargs[1]);
|
|
}
|
|
}
|
|
else if ( 'e' == fargs[1][0]
|
|
&& '\0' == fargs[1][1])
|
|
{
|
|
kBase = kNatural;
|
|
}
|
|
else
|
|
{
|
|
kBase = kOther;
|
|
base = mux_atof(fargs[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kBase = kCommon;
|
|
}
|
|
|
|
if ( kOther == kBase
|
|
&& base <= 1)
|
|
{
|
|
safe_str(S_("#-1 BASE OUT OF RANGE"), buff, bufc);
|
|
return;
|
|
}
|
|
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
if (val < 0.0)
|
|
{
|
|
safe_str(T("Ind"), buff, bufc);
|
|
return;
|
|
}
|
|
else if (0.0 == val)
|
|
{
|
|
safe_str(T("-Inf"), buff, bufc);
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
mux_FPRestore();
|
|
if (kCommon == kBase)
|
|
{
|
|
val = log10(val);
|
|
}
|
|
else if (kNatural == kBase)
|
|
{
|
|
val = log(val);
|
|
}
|
|
#ifdef HAVE_LOG2
|
|
else if (kBinary == kBase)
|
|
{
|
|
val = log2(val);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
val = log(val)/log(base);
|
|
}
|
|
mux_FPSet();
|
|
}
|
|
fval(buff, bufc, val);
|
|
}
|
|
|
|
FUNCTION(fun_sqrt)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
double val;
|
|
|
|
val = mux_atof(fargs[0]);
|
|
#ifndef HAVE_IEEE_FP_SNAN
|
|
if (val < 0.0)
|
|
{
|
|
safe_str(T("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)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_bool(is_real(fargs[0]), buff, bufc);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* israt: is the argument an rational?
|
|
*/
|
|
|
|
FUNCTION(fun_israt)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_bool(is_rational(fargs[0]), buff, bufc);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* isint: is the argument an integer?
|
|
*/
|
|
|
|
FUNCTION(fun_isint)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_bool(is_integer(fargs[0], nullptr), buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_and)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool val = true;
|
|
for (int i = 0; i < nfargs && val; i++)
|
|
{
|
|
val = isTRUE(mux_atoi64(fargs[i]));
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_or)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool val = false;
|
|
for (int i = 0; i < nfargs && !val; i++)
|
|
{
|
|
val = isTRUE(mux_atoi64(fargs[i]));
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_andbool)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool val = true;
|
|
for (int i = 0; i < nfargs && val; i++)
|
|
{
|
|
val = xlate(fargs[i]);
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_orbool)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
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;
|
|
LBuf temp = LBuf_Src("fun_cand");
|
|
for (int i = 0; i < nfargs && val && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
val = isTRUE(mux_atoi64(temp));
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_cor)
|
|
{
|
|
bool val = false;
|
|
LBuf temp = LBuf_Src("fun_cor");
|
|
for (int i = 0; i < nfargs && !val && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
val = isTRUE(mux_atoi64(temp));
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
// firstof(arg1, arg2, ...) — return the first true (non-zero) argument.
|
|
// Evaluates left-to-right, stops at first true. If none are true,
|
|
// returns the last evaluated value.
|
|
//
|
|
FUNCTION(fun_firstof)
|
|
{
|
|
if (0 == nfargs)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf temp = LBuf_Src("fun_firstof");
|
|
for (int i = 0; i < nfargs && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
|
|
if (isTRUE(mux_atoi64(temp)) || i == nfargs - 1)
|
|
{
|
|
safe_str(temp, buff, bufc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// strfirstof(arg1, arg2, ...) — return the first non-empty string argument.
|
|
// Evaluates left-to-right, stops at first non-empty. If all are empty,
|
|
// returns empty string.
|
|
//
|
|
FUNCTION(fun_strfirstof)
|
|
{
|
|
if (0 == nfargs)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf temp = LBuf_Src("fun_strfirstof");
|
|
for (int i = 0; i < nfargs && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
|
|
if ('\0' != temp[0] || i == nfargs - 1)
|
|
{
|
|
safe_str(temp, buff, bufc);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// allof(arg1, arg2, ...[, osep]) — return all true (non-zero) arguments,
|
|
// separated by osep. Last arg is the output separator if nfargs >= 2.
|
|
//
|
|
FUNCTION(fun_allof)
|
|
{
|
|
if (0 == nfargs)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Last argument is the output separator.
|
|
//
|
|
int nArgs = nfargs;
|
|
LBuf osep = LBuf_Src("fun_allof");
|
|
osep[0] = ' ';
|
|
osep[1] = '\0';
|
|
size_t nOsep = 1;
|
|
if (nfargs >= 2)
|
|
{
|
|
UTF8 *sp = osep;
|
|
mux_exec(fargs[nfargs - 1], LBUF_SIZE-1, osep, &sp, executor, caller,
|
|
enactor, eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*sp = '\0';
|
|
nOsep = sp - osep;
|
|
nArgs = nfargs - 1;
|
|
}
|
|
|
|
bool bFirst = true;
|
|
LBuf temp = LBuf_Src("fun_allof");
|
|
for (int i = 0; i < nArgs && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
|
|
if (isTRUE(mux_atoi64(temp)))
|
|
{
|
|
if (!bFirst)
|
|
{
|
|
safe_copy_buf(osep, nOsep, buff, bufc);
|
|
}
|
|
safe_str(temp, buff, bufc);
|
|
bFirst = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// strallof(arg1, arg2, ...[, osep]) — return all non-empty string arguments,
|
|
// separated by osep. Last arg is the output separator if nfargs >= 2.
|
|
//
|
|
FUNCTION(fun_strallof)
|
|
{
|
|
if (0 == nfargs)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int nArgs = nfargs;
|
|
LBuf osep = LBuf_Src("fun_strallof");
|
|
osep[0] = ' ';
|
|
osep[1] = '\0';
|
|
size_t nOsep = 1;
|
|
if (nfargs >= 2)
|
|
{
|
|
UTF8 *sp = osep;
|
|
mux_exec(fargs[nfargs - 1], LBUF_SIZE-1, osep, &sp, executor, caller,
|
|
enactor, eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*sp = '\0';
|
|
nOsep = sp - osep;
|
|
nArgs = nfargs - 1;
|
|
}
|
|
|
|
bool bFirst = true;
|
|
LBuf temp = LBuf_Src("fun_strallof");
|
|
for (int i = 0; i < nArgs && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
|
|
if ('\0' != temp[0])
|
|
{
|
|
if (!bFirst)
|
|
{
|
|
safe_copy_buf(osep, nOsep, buff, bufc);
|
|
}
|
|
safe_str(temp, buff, bufc);
|
|
bFirst = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_candbool)
|
|
{
|
|
bool val = true;
|
|
LBuf temp = LBuf_Src("fun_candbool");
|
|
for (int i = 0; i < nfargs && val && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
val = xlate(temp);
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_corbool)
|
|
{
|
|
bool val = false;
|
|
LBuf temp = LBuf_Src("fun_corbool");
|
|
for (int i = 0; i < nfargs && !val && !alarm_clock.alarmed; i++)
|
|
{
|
|
UTF8 *bp = temp.get();
|
|
mux_exec(fargs[i], LBUF_SIZE-1, temp, &bp, executor, caller, enactor,
|
|
eval|EV_STRIP_CURLY|EV_FCHECK|EV_EVAL, cargs, ncargs);
|
|
*bp = '\0';
|
|
val = xlate(temp);
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_xor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool val = false;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
// Test truthiness on the full 64-bit value, matching and()/or()/
|
|
// lxor(); narrowing to int drops the high bits of large operands.
|
|
//
|
|
bool tval = isTRUE(mux_atoi64(fargs[i]));
|
|
val = (val && !tval) || (!val && tval);
|
|
}
|
|
safe_bool(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_not)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
safe_bool(!xlate(fargs[0]), buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_t)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if ( nfargs <= 0
|
|
|| fargs[0][0] == '\0')
|
|
{
|
|
safe_chr('0', buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_bool(xlate(fargs[0]), buff, bufc);
|
|
}
|
|
}
|
|
static const UTF8 *bigones[] =
|
|
{
|
|
T(""),
|
|
T("thousand"),
|
|
T("million"),
|
|
T("billion"),
|
|
T("trillion")
|
|
};
|
|
|
|
static const UTF8 *singles[] =
|
|
{
|
|
T(""),
|
|
T("one"),
|
|
T("two"),
|
|
T("three"),
|
|
T("four"),
|
|
T("five"),
|
|
T("six"),
|
|
T("seven"),
|
|
T("eight"),
|
|
T("nine")
|
|
};
|
|
|
|
static const UTF8 *teens[] =
|
|
{
|
|
T("ten"),
|
|
T("eleven"),
|
|
T("twelve"),
|
|
T("thirteen"),
|
|
T("fourteen"),
|
|
T("fifteen"),
|
|
T("sixteen"),
|
|
T("seventeen"),
|
|
T("eighteen"),
|
|
T("nineteen")
|
|
};
|
|
|
|
static const UTF8 *tens[] =
|
|
{
|
|
T(""),
|
|
T(""),
|
|
T("twenty"),
|
|
T("thirty"),
|
|
T("forty"),
|
|
T("fifty"),
|
|
T("sixty"),
|
|
T("seventy"),
|
|
T("eighty"),
|
|
T("ninety")
|
|
};
|
|
|
|
static const UTF8 *th_prefix[] =
|
|
{
|
|
T(""),
|
|
T("ten"),
|
|
T("hundred")
|
|
};
|
|
|
|
class CSpellNum
|
|
{
|
|
public:
|
|
void SpellNum(const UTF8 *p, UTF8 *buff_arg, UTF8 **bufc_arg);
|
|
|
|
private:
|
|
void TwoDigits(const UTF8 *p);
|
|
void ThreeDigits(const UTF8 *p, size_t iBigOne);
|
|
void ManyDigits(size_t n, const UTF8 *p, bool bHundreds);
|
|
void FractionalDigits(size_t n, const UTF8 *p);
|
|
|
|
void StartWord(void);
|
|
void AddWord(const UTF8 *p);
|
|
|
|
UTF8 *buff;
|
|
UTF8 **bufc;
|
|
bool bNeedSpace;
|
|
};
|
|
|
|
void CSpellNum::StartWord(void)
|
|
{
|
|
if (bNeedSpace)
|
|
{
|
|
safe_chr(' ', buff, bufc);
|
|
}
|
|
bNeedSpace = true;
|
|
}
|
|
|
|
void CSpellNum::AddWord(const UTF8 *p)
|
|
{
|
|
safe_str(p, buff, bufc);
|
|
}
|
|
|
|
// Handle two-character sequences.
|
|
//
|
|
void CSpellNum::TwoDigits(const UTF8 *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(T("-"));
|
|
AddWord(singles[n1]);
|
|
}
|
|
}
|
|
|
|
// Handle three-character sequences.
|
|
//
|
|
void CSpellNum::ThreeDigits(const UTF8 *p, size_t 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(T("hundred"));
|
|
}
|
|
TwoDigits(p+1);
|
|
if (iBigOne > 0)
|
|
{
|
|
StartWord();
|
|
AddWord(bigones[iBigOne]);
|
|
}
|
|
}
|
|
|
|
// Handle a series of patterns of three.
|
|
//
|
|
void CSpellNum::ManyDigits(size_t n, const UTF8 *p, bool bHundreds)
|
|
{
|
|
// Handle special Hundreds cases.
|
|
//
|
|
if ( bHundreds
|
|
&& n == 4
|
|
&& p[1] != '0')
|
|
{
|
|
TwoDigits(p);
|
|
StartWord();
|
|
AddWord(T("hundred"));
|
|
TwoDigits(p+2);
|
|
return;
|
|
}
|
|
|
|
// Handle normal cases.
|
|
//
|
|
size_t ndiv = ((n + 2) / 3) - 1;
|
|
size_t nrem = n % 3;
|
|
UTF8 buf[3];
|
|
if (nrem == 0)
|
|
{
|
|
nrem = 3;
|
|
}
|
|
|
|
size_t 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(size_t n, const UTF8 *p)
|
|
{
|
|
ManyDigits(n, p, false);
|
|
if ( 0 < n
|
|
&& n < 15)
|
|
{
|
|
size_t d = n / 3;
|
|
size_t r = n % 3;
|
|
StartWord();
|
|
if (r != 0)
|
|
{
|
|
AddWord(th_prefix[r]);
|
|
if (d != 0)
|
|
{
|
|
AddWord(T("-"));
|
|
}
|
|
}
|
|
AddWord(bigones[d]);
|
|
AddWord(T("th"));
|
|
int64_t i64 = mux_atoi64(p);
|
|
if (i64 != 1)
|
|
{
|
|
AddWord(T("s"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSpellNum::SpellNum(const UTF8 *number, UTF8 *buff_arg, UTF8 **bufc_arg)
|
|
{
|
|
buff = buff_arg;
|
|
bufc = bufc_arg;
|
|
bNeedSpace = false;
|
|
|
|
// Trim Spaces from beginning.
|
|
//
|
|
while (mux_isspace(*number))
|
|
{
|
|
number++;
|
|
}
|
|
|
|
if (*number == '-')
|
|
{
|
|
StartWord();
|
|
AddWord(T("negative"));
|
|
number++;
|
|
}
|
|
|
|
// Trim Zeroes from Beginning.
|
|
//
|
|
while (*number == '0')
|
|
{
|
|
number++;
|
|
}
|
|
|
|
const UTF8 *pA = number;
|
|
while (mux_isdigit(*number))
|
|
{
|
|
number++;
|
|
}
|
|
size_t nA = number - pA;
|
|
|
|
const UTF8 *pB = nullptr;
|
|
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(S_("#-1 ARGUMENT MUST BE A NUMBER"), buff, bufc);
|
|
return;
|
|
}
|
|
|
|
if (nA == 0)
|
|
{
|
|
if (nB == 0)
|
|
{
|
|
StartWord();
|
|
AddWord(T("zero"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ManyDigits(nA, pA, true);
|
|
if (nB)
|
|
{
|
|
StartWord();
|
|
AddWord(T("and"));
|
|
}
|
|
}
|
|
if (nB)
|
|
{
|
|
FractionalDigits(nB, pB);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_spellnum)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
CSpellNum sn;
|
|
sn.SpellNum(fargs[0], buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_roman)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
const UTF8 *number = fargs[0];
|
|
|
|
// Trim Spaces from beginning.
|
|
//
|
|
while (mux_isspace(*number))
|
|
{
|
|
number++;
|
|
}
|
|
|
|
// Trim Zeroes from Beginning.
|
|
//
|
|
while (*number == '0')
|
|
{
|
|
number++;
|
|
}
|
|
|
|
const UTF8 *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(S_("#-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 UTF8 aLetters[4][3] =
|
|
{
|
|
{ 'I', 'V', 'X' },
|
|
{ 'X', 'L', 'C' },
|
|
{ 'C', 'D', 'M' },
|
|
{ 'M', ' ', ' ' }
|
|
};
|
|
|
|
static const UTF8 *aCode[10] =
|
|
{
|
|
T(""),
|
|
T("1"),
|
|
T("11"),
|
|
T("111"),
|
|
T("12"),
|
|
T("2"),
|
|
T("21"),
|
|
T("211"),
|
|
T("2111"),
|
|
T("13")
|
|
};
|
|
|
|
while (nA--)
|
|
{
|
|
const UTF8 *pCode = aCode[*pA - '0'];
|
|
const UTF8 *pLetters = aLetters[nA];
|
|
|
|
while (*pCode)
|
|
{
|
|
safe_chr(pLetters[*pCode - '1'], buff, bufc);
|
|
pCode++;
|
|
}
|
|
pA++;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* List-based numeric functions.
|
|
*/
|
|
|
|
FUNCTION(fun_land)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bValue = true;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp && bValue)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
bValue = isTRUE(mux_atoi64(curr));
|
|
}
|
|
}
|
|
safe_bool(bValue, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bValue = false;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp && !bValue)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
bValue = isTRUE(mux_atoi64(curr));
|
|
}
|
|
}
|
|
safe_bool(bValue, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lxor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
bool bValue = false;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
bool bCurr = isTRUE(mux_atoi64(curr));
|
|
bValue = (bValue && !bCurr) || (!bValue && bCurr);
|
|
}
|
|
}
|
|
safe_bool(bValue, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lband)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = UINT64_MAX;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
if (!is_integer(curr, nullptr))
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
val &= mux_atoi64(curr);
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lbor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = 0;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
if (!is_integer(curr, nullptr))
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
val |= mux_atoi64(curr);
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_lbxor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = 0;
|
|
if (0 < nfargs)
|
|
{
|
|
SEP sep;
|
|
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LBuf scList = LBuf_Src("fun_lbool.list");
|
|
UTF8 *cp = trim_space_sep(list_copy_for_split(scList, fargs[0]), sep);
|
|
while (cp)
|
|
{
|
|
UTF8 *curr = split_token(&cp, sep);
|
|
if (!is_integer(curr, nullptr))
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
val ^= mux_atoi64(curr);
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_band)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = UINT64_MAX;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
if (is_integer(fargs[i], nullptr))
|
|
{
|
|
val &= mux_atoi64(fargs[i]);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_bor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = 0;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
if (is_integer(fargs[i], nullptr))
|
|
{
|
|
val |= mux_atoi64(fargs[i]);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_bnand)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(nfargs);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
if ( is_integer(fargs[0], nullptr)
|
|
&& is_integer(fargs[1], nullptr))
|
|
{
|
|
int64_t a = mux_atoi64(fargs[0]);
|
|
int64_t b = mux_atoi64(fargs[1]);
|
|
safe_i64toa(a & ~(b), buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
}
|
|
}
|
|
|
|
FUNCTION(fun_bxor)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint64_t val = 0;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
if (is_integer(fargs[i], nullptr))
|
|
{
|
|
val ^= mux_atoi64(fargs[i]);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 ARGUMENTS MUST BE INTEGERS"), buff, bufc);
|
|
return;
|
|
}
|
|
}
|
|
safe_i64toa(val, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_crc32)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
uint32_t ulCRC32 = 0;
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
size_t n = strlen(reinterpret_cast<const char *>(fargs[i]));
|
|
ulCRC32 = CRC32_ProcessBuffer(ulCRC32, fargs[i], n);
|
|
}
|
|
safe_i64toa(ulCRC32, buff, bufc);
|
|
}
|
|
|
|
void safe_hex(uint8_t md[], size_t len, bool bUpper, UTF8 *buff, UTF8 **bufc)
|
|
{
|
|
std::vector<UTF8> buf((len * 2) + 1);
|
|
|
|
int bufoffset = 0;
|
|
const UTF8 *Digits16 = bUpper ? Digits16U : Digits16L;
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
uint8_t c = md[i];
|
|
buf[bufoffset++] = Digits16[(c >> 4) & 0x0F];
|
|
buf[bufoffset++] = Digits16[(c ) & 0x0F];
|
|
}
|
|
buf[bufoffset] = '\0';
|
|
safe_str(buf.data(), buff, bufc);
|
|
}
|
|
|
|
void sha1_helper(int nfargs, const UTF8 * const fargs[], UTF8 *buff, UTF8 **bufc)
|
|
{
|
|
#ifdef UNIX_DIGEST
|
|
uint8_t md[EVP_MAX_MD_SIZE];
|
|
#else
|
|
uint8_t md[MUX_SHA1_DIGEST_LENGTH];
|
|
#endif
|
|
unsigned int len = 0;
|
|
std::vector<size_t> lens(nfargs);
|
|
for (int i = 0; i < nfargs; i++)
|
|
{
|
|
lens[i] = strlen(reinterpret_cast<const char *>(fargs[i]));
|
|
}
|
|
if (!mux_sha1_digest(fargs, lens.data(), nfargs, md, &len))
|
|
{
|
|
safe_str(S_("#-1 UNSUPPORTED"), buff, bufc);
|
|
return;
|
|
}
|
|
safe_hex(md, len, true, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_sha1)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
sha1_helper(nfargs, fargs, buff, bufc);
|
|
}
|
|
|
|
FUNCTION(fun_digest)
|
|
{
|
|
UNUSED_PARAMETER(executor);
|
|
UNUSED_PARAMETER(caller);
|
|
UNUSED_PARAMETER(enactor);
|
|
UNUSED_PARAMETER(eval);
|
|
UNUSED_PARAMETER(cargs);
|
|
UNUSED_PARAMETER(ncargs);
|
|
|
|
#ifdef UNIX_DIGEST
|
|
EVP_MD_CTX *ctx;
|
|
#if HAVE_EVP_MD_CTX_NEW
|
|
ctx = EVP_MD_CTX_new();
|
|
#elif HAVE_EVP_MD_CTX_CREATE
|
|
ctx = EVP_MD_CTX_create();
|
|
#else
|
|
#error Need EVP_MD_CTX_new() or EVP_MD_CTX_create().
|
|
#endif
|
|
|
|
// Resolve the (user-supplied) digest name. On OpenSSL 3.0+ use the
|
|
// provider-native EVP_MD_fetch(): unlike the legacy EVP_get_digestbyname(),
|
|
// it resolves algorithm aliases such as "sha-1" deterministically from a
|
|
// cold process, with no dependence on an earlier digest call having lazily
|
|
// loaded the default provider (#1961). A fetched EVP_MD is a reference that
|
|
// must be freed; the legacy pointer is a static const that must not be.
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
EVP_MD *mp = EVP_MD_fetch(nullptr, reinterpret_cast<const char *>(fargs[0]), nullptr);
|
|
#else
|
|
const EVP_MD *mp = EVP_get_digestbyname(reinterpret_cast<const char *>(fargs[0]));
|
|
#endif
|
|
if (nullptr == mp)
|
|
{
|
|
// Release the context allocated above before bailing (#1961 leak fix).
|
|
#if HAVE_EVP_MD_CTX_NEW
|
|
EVP_MD_CTX_free(ctx);
|
|
#elif HAVE_EVP_MD_CTX_CREATE
|
|
EVP_MD_CTX_destroy(ctx);
|
|
#endif
|
|
safe_str(S_("#-1 UNSUPPORTED DIGEST TYPE"), buff, bufc);
|
|
return;
|
|
}
|
|
|
|
EVP_DigestInit(ctx, mp);
|
|
|
|
int i;
|
|
for (i = 1; i < nfargs; i++)
|
|
{
|
|
EVP_DigestUpdate(ctx, fargs[i], strlen(reinterpret_cast<const char *>(fargs[i])));
|
|
}
|
|
|
|
unsigned int len = 0;
|
|
uint8_t md[EVP_MAX_MD_SIZE];
|
|
EVP_DigestFinal(ctx, md, &len);
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
EVP_MD_free(mp);
|
|
#endif
|
|
#if HAVE_EVP_MD_CTX_NEW
|
|
EVP_MD_CTX_free(ctx);
|
|
#elif HAVE_EVP_MD_CTX_CREATE
|
|
EVP_MD_CTX_destroy(ctx);
|
|
#else
|
|
#error Need EVP_MD_CTX_new() or EVP_MD_CTX_create().
|
|
#endif
|
|
safe_hex(md, len, true, buff, bufc);
|
|
#else
|
|
// CNG backend (#1963): sha1/sha256/sha384/sha512/md5, resolved by
|
|
// mux_digest. Same #-1 on unknown names as the OpenSSL side.
|
|
uint8_t md[MUX_MAX_DIGEST_LENGTH];
|
|
unsigned int len = 0;
|
|
std::vector<size_t> lens(nfargs > 1 ? nfargs - 1 : 0);
|
|
for (int i = 1; i < nfargs; i++)
|
|
{
|
|
lens[i-1] = strlen(reinterpret_cast<const char *>(fargs[i]));
|
|
}
|
|
if (mux_digest(fargs[0], const_cast<const UTF8 **>(fargs + 1), lens.data(),
|
|
nfargs - 1, md, &len))
|
|
{
|
|
safe_hex(md, len, true, buff, bufc);
|
|
}
|
|
else
|
|
{
|
|
safe_str(S_("#-1 UNSUPPORTED DIGEST TYPE"), buff, bufc);
|
|
}
|
|
#endif // UNIX_DIGEST
|
|
}
|