/** * \file funmath.c * * \brief Mathematical functions for mushcode. * * */ #include "copyrite.h" #include "config.h" #include <math.h> #include <string.h> #include <ctype.h> #include "conf.h" #include "externs.h" #include "parse.h" #include "htab.h" #include "confmagic.h" #ifdef WIN32 #pragma warning( disable : 4761) /* NJG: disable warning re conversion */ #endif #ifndef M_PI /** The ratio of the circumference of a circle to its denominator. */ #define M_PI 3.14159265358979323846264338327 #endif static void do_spellnum(char *num, unsigned int len, char **buff, char ***bp); static int nval_sort(const void *, const void *); static NVAL find_median(NVAL *, int); /** Declaration macro for math functions */ #define MATH_FUNC(func) static void func(char **ptr, int nptr, char *buff, char **bp) /** Prototype macro for math functions */ #define MATH_PROTO(func) static void func (char **ptr, int nptr, char *buff, char **bp) HASHTAB htab_math; /**< Math function hash table */ /** A math function. */ typedef struct { const char *name; /**< Name of the function. */ void (*func) (char **, int, char *, char **); /**< Pointer to function code. */ } MATH; static void math_hash_insert(const char *, MATH *); static MATH *math_hash_lookup(char *); static NVAL angle_to_rad(NVAL angle, const char *from); static NVAL rad_to_angle(NVAL angle, const char *to); static double frac(double v, double *RESTRICT n, double *RESTRICT d, double error); void init_math_hashtab(void); extern int format_long(long n, char *buff, char **bp, int maxlen, int base); MATH_PROTO(math_add); MATH_PROTO(math_sub); MATH_PROTO(math_mul); MATH_PROTO(math_div); MATH_PROTO(math_floordiv); MATH_PROTO(math_remainder); MATH_PROTO(math_modulo); MATH_PROTO(math_min); MATH_PROTO(math_max); MATH_PROTO(math_and); MATH_PROTO(math_nand); MATH_PROTO(math_or); MATH_PROTO(math_nor); MATH_PROTO(math_xor); MATH_PROTO(math_band); MATH_PROTO(math_bor); MATH_PROTO(math_bxor); MATH_PROTO(math_fdiv); MATH_PROTO(math_mean); MATH_PROTO(math_median); MATH_PROTO(math_stddev); /* ARGSUSED */ FUNCTION(fun_ctu) { NVAL angle; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } if (!args[1] || !args[2]) { safe_str(T("#-1 INVALID ANGLE TYPE"), buff, bp); return; } angle = angle_to_rad(parse_number(args[0]), args[1]); safe_number(rad_to_angle(angle, args[2]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_add) { math_add(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_sub) { math_sub(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_mul) { math_mul(args, nargs, buff, bp); } /* TO-DO: I have better code for comparing floating-point numbers lying around somewhere. The idea is that numbers that are very close can be 'close enough' to be equal or whatever, without having to be exactly the same. */ /* ARGSUSED */ FUNCTION(fun_gt) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) > parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_gte) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) >= parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_lt) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) < parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_lte) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) <= parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_eq) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) == parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_neq) { if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } safe_boolean(parse_number(args[0]) != parse_number(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_max) { math_max(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_min) { math_min(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_sign) { NVAL x; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } x = parse_number(args[0]); if (x == 0) safe_chr('0', buff, bp); else if (x > 0) safe_chr('1', buff, bp); else safe_str("-1", buff, bp); } /* ARGSUSED */ FUNCTION(fun_shl) { if (!is_uinteger(args[0]) || !is_uinteger(args[1])) { safe_str(T(e_uints), buff, bp); return; } safe_uinteger(parse_uinteger(args[0]) << parse_uinteger(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_shr) { if (!is_uinteger(args[0]) || !is_uinteger(args[1])) { safe_str(T(e_uints), buff, bp); return; } safe_uinteger(parse_uinteger(args[0]) >> parse_uinteger(args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_inc) { int num; char *p; /* Handle the case of a pure number */ if (is_strict_integer(args[0])) { safe_integer(parse_integer(args[0]) + 1, buff, bp); return; } /* Handle a null string */ if (!*args[0]) { safe_str(NULL_EQ_ZERO ? "1" : T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp); return; } p = args[0] + arglens[0] - 1; if (!isdigit((unsigned char) *p)) { if (NULL_EQ_ZERO) { safe_str(args[0], buff, bp); safe_str("1", buff, bp); } else safe_str(T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp); return; } while ((isdigit((unsigned char) *p) || (*p == '-')) && p != args[0]) { if (*p == '-') { p--; break; } p--; } /* p now points to the last non-numeric character in the string */ if (p == args[0] && (isdigit((unsigned char) *p) || (*p == '-'))) { /* Special case - it's all digits, but out of range. */ safe_str(T(e_range), buff, bp); return; } /* Move it to the first numeric character */ p++; num = parse_integer(p) + 1; *p = '\0'; safe_str(args[0], buff, bp); safe_integer(num, buff, bp); } /* ARGSUSED */ FUNCTION(fun_dec) { int num; char *p; /* Handle the case of a pure number */ if (is_strict_integer(args[0])) { safe_integer(parse_integer(args[0]) - 1, buff, bp); return; } /* Handle a null string */ if (!*args[0]) { safe_str(NULL_EQ_ZERO ? "-1" : T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp); return; } p = args[0] + arglens[0] - 1; if (!isdigit((unsigned char) *p)) { if (NULL_EQ_ZERO) { safe_str(args[0], buff, bp); safe_str("-1", buff, bp); } else safe_str(T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp); return; } while ((isdigit((unsigned char) *p) || (*p == '-')) && p != args[0]) { if (*p == '-') { p--; break; } p--; } /* p now points to the last non-numeric character in the string */ if (p == args[0] && (isdigit((unsigned char) *p) || (*p == '-'))) { /* Special case - it's all digits, but out of range. */ safe_str(T(e_range), buff, bp); return; } /* Move it to the first numeric character */ p++; num = parse_integer(p) - 1; *p = '\0'; safe_str(args[0], buff, bp); safe_integer(num, buff, bp); } /* ARGSUSED */ FUNCTION(fun_trunc) { /* This function does not have the non-number check because * the help file explicitly states that this function can * be used to turn "101dalmations" into "101". */ safe_integer(parse_integer(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_div) { math_div(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_floordiv) { math_floordiv(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_modulo) { math_modulo(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_remainder) { math_remainder(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_abs) { if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } safe_number(fabs(parse_number(args[0])), buff, bp); } /* ARGSUSED */ FUNCTION(fun_dist2d) { NVAL d1, d2, r; if (!is_number(args[0]) || !is_number(args[1]) || !is_number(args[2]) || !is_number(args[3])) { safe_str(T(e_nums), buff, bp); return; } d1 = parse_number(args[0]) - parse_number(args[2]); d2 = parse_number(args[1]) - parse_number(args[3]); r = d1 * d1 + d2 * d2; #ifndef HAS_IEEE_MATH /* You can overflow, which is bad. */ if (r < 0) { safe_str(T("#-1 OVERFLOW ERROR"), buff, bp); return; } #endif safe_number(sqrt(r), buff, bp); } /* ARGSUSED */ FUNCTION(fun_dist3d) { NVAL d1, d2, d3, r; if (!is_number(args[0]) || !is_number(args[1]) || !is_number(args[2]) || !is_number(args[3]) || !is_number(args[4]) || !is_number(args[5])) { safe_str(T(e_nums), buff, bp); return; } d1 = parse_number(args[0]) - parse_number(args[3]); d2 = parse_number(args[1]) - parse_number(args[4]); d3 = parse_number(args[2]) - parse_number(args[5]); r = d1 * d1 + d2 * d2 + d3 * d3; #ifndef HAS_IEEE_MATH /* You can overflow, which is bad. */ if (r < 0) { safe_str(T("#-1 OVERFLOW ERROR"), buff, bp); return; } #endif safe_number(sqrt(r), buff, bp); } /* ------------------------------------------------------------------------ * Dune's vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM * VCRAMER? * Vectors are space-separated numbers. */ /* ARGSUSED */ FUNCTION(fun_vmax) { char *p1, *p2; char *start; char sep; NVAL a, b; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* max the vectors */ start = *bp; a = parse_number(split_token(&p1, sep)); b = parse_number(split_token(&p2, sep)); safe_number((a > b) ? a : b, buff, bp); while (p1 && p2) { safe_chr(sep, buff, bp); a = parse_number(split_token(&p1, sep)); b = parse_number(split_token(&p2, sep)); safe_number((a > b) ? a : b, buff, bp); } /* make sure vectors were the same length */ if (p1 || p2) { *bp = start; safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } } /* ARGSUSED */ FUNCTION(fun_vmin) { char *p1, *p2; char *start; char sep; NVAL a, b; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* max the vectors */ start = *bp; a = parse_number(split_token(&p1, sep)); b = parse_number(split_token(&p2, sep)); safe_number((a < b) ? a : b, buff, bp); while (p1 && p2) { safe_chr(sep, buff, bp); a = parse_number(split_token(&p1, sep)); b = parse_number(split_token(&p2, sep)); safe_number((a < b) ? a : b, buff, bp); } /* make sure vectors were the same length */ if (p1 || p2) { *bp = start; safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } } /* ARGSUSED */ FUNCTION(fun_vadd) { char *p1, *p2; char *start; char sep; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* add the vectors */ start = *bp; safe_number(parse_number(split_token(&p1, sep)) + parse_number(split_token(&p2, sep)), buff, bp); while (p1 && p2) { safe_chr(sep, buff, bp); safe_number(parse_number(split_token(&p1, sep)) + parse_number(split_token(&p2, sep)), buff, bp); } /* make sure vectors were the same length */ if (p1 || p2) { *bp = start; safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } } /* ARGSUSED */ FUNCTION(fun_vsub) { char *p1, *p2; char *start; char sep; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* subtract the vectors */ start = *bp; safe_number(parse_number(split_token(&p1, sep)) - parse_number(split_token(&p2, sep)), buff, bp); while (p1 && p2) { safe_chr(sep, buff, bp); safe_number(parse_number(split_token(&p1, sep)) - parse_number(split_token(&p2, sep)), buff, bp); } /* make sure vectors were the same length */ if (p1 || p2) { *bp = start; safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } } /* ARGSUSED */ FUNCTION(fun_vmul) { NVAL e1, e2; char *p1, *p2; char *start; char sep; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* multiply the vectors */ start = *bp; e1 = parse_number(split_token(&p1, sep)); e2 = parse_number(split_token(&p2, sep)); if (!p1) { /* scalar * vector */ safe_number(e1 * e2, buff, bp); while (p2) { safe_chr(sep, buff, bp); safe_number(e1 * parse_number(split_token(&p2, sep)), buff, bp); } } else if (!p2) { /* vector * scalar */ safe_number(e1 * e2, buff, bp); while (p1) { safe_chr(sep, buff, bp); safe_number(parse_number(split_token(&p1, sep)) * e2, buff, bp); } } else { /* vector * vector elementwise product */ safe_number(e1 * e2, buff, bp); while (p1 && p2) { safe_chr(sep, buff, bp); safe_number (parse_number(split_token(&p1, sep)) * parse_number(split_token(&p2, sep)), buff, bp); } /* make sure vectors were the same length */ if (p1 || p2) { *bp = start; safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } } } /* ARGSUSED */ FUNCTION(fun_vdot) { NVAL product; char *p1, *p2; char sep; /* return if a list is empty */ if (!args[0] || !args[1]) return; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; p1 = trim_space_sep(args[0], sep); p2 = trim_space_sep(args[1], sep); /* return if a list is empty */ if (!*p1 || !*p2) return; /* multiply the vectors */ product = 0; while (p1 && p2) { product += parse_number(split_token(&p1, sep)) * parse_number(split_token(&p2, sep)); } if (p1 || p2) { safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp); return; } safe_number(product, buff, bp); } /* ARGSUSED */ FUNCTION(fun_vmag) { NVAL num, sum; char *p1; char sep; /* return if a list is empty */ if (!args[0]) return; if (!delim_check(buff, bp, nargs, args, 2, &sep)) return; p1 = trim_space_sep(args[0], sep); /* return if a list is empty */ if (!*p1) return; /* sum the squares */ num = parse_number(split_token(&p1, sep)); sum = num * num; while (p1) { num = parse_number(split_token(&p1, sep)); sum += num * num; } safe_number(sqrt(sum), buff, bp); } /* ARGSUSED */ FUNCTION(fun_vunit) { NVAL num, sum; char tbuf[BUFFER_LEN]; char *p1; char sep; /* return if a list is empty */ if (!args[0]) return; if (!delim_check(buff, bp, nargs, args, 2, &sep)) return; p1 = trim_space_sep(args[0], sep); /* return if a list is empty */ if (!*p1) return; /* copy the vector, since we have to walk it twice... */ strcpy(tbuf, p1); /* find the magnitude */ num = parse_number(split_token(&p1, sep)); sum = num * num; while (p1) { num = parse_number(split_token(&p1, sep)); sum += num * num; } sum = sqrt(sum); if (!sum) { /* zero vector */ p1 = tbuf; safe_chr('0', buff, bp); while (split_token(&p1, sep), p1) { safe_chr(sep, buff, bp); safe_chr('0', buff, bp); } return; } /* now make the unit vector */ p1 = tbuf; safe_number(parse_number(split_token(&p1, sep)) / sum, buff, bp); while (p1) { safe_chr(sep, buff, bp); safe_number(parse_number(split_token(&p1, sep)) / sum, buff, bp); } } FUNCTION(fun_vcross) { char sep = ' '; char *v1[BUFFER_LEN / 2], *v2[BUFFER_LEN / 2]; int v1len, v2len, n; NVAL vec1[3], vec2[3], cross[3]; if (!delim_check(buff, bp, nargs, args, 3, &sep)) return; v1len = list2arr(v1, BUFFER_LEN / 2, args[0], sep); v2len = list2arr(v2, BUFFER_LEN / 2, args[1], sep); if (v1len != 3 || v2len != 3) { safe_str(T("#-1 VECTORS MUST BE THREE-DIMENSIONAL"), buff, bp); return; } for (n = 0; n < 3; n++) { vec1[n] = parse_number(v1[n]); vec2[n] = parse_number(v2[n]); } cross[0] = vec1[1] * vec2[2] - vec2[1] * vec1[2]; cross[1] = vec1[2] * vec2[0] - vec2[2] * vec1[0]; cross[2] = vec1[0] * vec2[1] - vec2[0] * vec1[1]; safe_number(cross[0], buff, bp); safe_chr(sep, buff, bp); safe_number(cross[1], buff, bp); safe_chr(sep, buff, bp); safe_number(cross[2], buff, bp); } /* ARGSUSED */ FUNCTION(fun_fdiv) { math_fdiv(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_fmod) { NVAL x, y; if (!is_strict_number(args[0]) || !is_strict_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } y = parse_number(args[1]); if (y == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } x = parse_number(args[0]); safe_number(fmod(x, y), buff, bp); } /* ARGSUSED */ FUNCTION(fun_floor) { if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } safe_number(floor(parse_number(args[0])), buff, bp); } /* ARGSUSED */ FUNCTION(fun_ceil) { if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } safe_number(ceil(parse_number(args[0])), buff, bp); } /* ARGSUSED */ FUNCTION(fun_round) { char temp[BUFFER_LEN]; int places; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } if (nargs == 2) { if (!is_integer(args[1])) { safe_str(T(e_int), buff, bp); return; } places = parse_integer(args[1]); } else places = 0; if (places < 0) places = 0; else if (places > FLOAT_PRECISION) places = FLOAT_PRECISION; /* The 0.0000001 is a kludge because .15 gets represented as .149999... * on many systems, and rounds down to .1. Lame. */ sprintf(temp, "%.*f", places, parse_number(args[0]) + 0.0000001); /* Handle the bizarre "-0" sprintf problem. */ if (!strcmp(temp, "-0")) safe_chr('0', buff, bp); else safe_str(temp, buff, bp); } /* ARGSUSED */ FUNCTION(fun_pi) { safe_number(M_PI, buff, bp); } /* ARGSUSED */ FUNCTION(fun_e) { safe_number(2.71828182845904523536, buff, bp); } /* ARGSUSED */ FUNCTION(fun_sin) { NVAL angle; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } angle = angle_to_rad(parse_number(args[0]), args[1]); safe_number(sin(angle), buff, bp); } /* ARGSUSED */ FUNCTION(fun_asin) { NVAL num; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } num = parse_number(args[0]); if ((num < -1) || (num > 1)) { safe_str(T(e_range), buff, bp); return; } safe_number(rad_to_angle(asin(num), args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_cos) { NVAL angle; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } angle = angle_to_rad(parse_number(args[0]), args[1]); safe_number(cos(angle), buff, bp); } /* ARGSUSED */ FUNCTION(fun_acos) { NVAL num; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } num = parse_number(args[0]); if ((num < -1) || (num > 1)) { safe_str(T(e_range), buff, bp); return; } safe_number(rad_to_angle(acos(num), args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_tan) { NVAL angle; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } angle = angle_to_rad(parse_number(args[0]), args[1]); safe_number(tan(angle), buff, bp); } /* ARGSUSED */ FUNCTION(fun_atan) { NVAL angle; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } angle = parse_number(args[0]); safe_number(rad_to_angle(atan(angle), args[1]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_atan2) { NVAL x, y; if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_num), buff, bp); return; } x = parse_number(args[0]); y = parse_number(args[1]); safe_number(rad_to_angle(atan2(x, y), args[2]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_exp) { if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } safe_number(exp(parse_number(args[0])), buff, bp); } /* ARGSUSED */ FUNCTION(fun_power) { NVAL num; NVAL m; if (!is_number(args[0]) || !is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } num = parse_number(args[0]); m = parse_number(args[1]); if (num < 0 && (m != (int) m)) { safe_str(T("#-1 FRACTIONAL POWER OF NEGATIVE"), buff, bp); return; } #ifndef HAS_IEEE_MATH if ((num > 100) || (m > 100)) { safe_str(T(e_range), buff, bp); return; } #endif safe_number(pow(num, m), buff, bp); } /* ARGSUSED */ FUNCTION(fun_ln) { NVAL num; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } num = parse_number(args[0]); #ifndef HAS_IEEE_MATH /* log(0) is bad for you */ if (num == 0) { safe_str(T("#-1 INFINITY"), buff, bp); return; } #endif if (num < 0) { safe_str(T(e_range), buff, bp); return; } safe_number(log(num), buff, bp); } /* ARGSUSED */ FUNCTION(fun_log) { NVAL num, base; if (!is_number(args[0])) { safe_str(T(e_nums), buff, bp); return; } num = parse_number(args[0]); #ifndef HAS_IEEE_MATH /* log(0) is bad for you */ if (num == 0) { safe_str(T("#-1 INFINITY"), buff, bp); return; } #endif if (num < 0) { safe_str(T(e_range), buff, bp); return; } if (nargs == 2) { if (!is_number(args[1])) { safe_str(T(e_nums), buff, bp); return; } base = parse_number(args[1]); if (base <= 1) { safe_str(T("#-1 BASE OUT OF RANGE"), buff, bp); return; } safe_number(log(num) / log(base), buff, bp); } else safe_number(log10(num), buff, bp); } /* ARGSUSED */ FUNCTION(fun_sqrt) { NVAL num; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } num = parse_number(args[0]); if (num < 0) { safe_str(T("#-1 IMAGINARY NUMBER"), buff, bp); return; } safe_number(sqrt(num), buff, bp); } FUNCTION(fun_root) { NVAL root, x; int n; int sign = 0; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } if (!is_integer(args[1])) { safe_str(T(e_int), buff, bp); return; } x = parse_number(args[0]); n = parse_integer(args[1]); if (n < 0) { safe_str(T("#-1 ROOT OUT OF RANGE"), buff, bp); return; } if (x < 0) { if (n & 1) { /* Odd root */ sign = 1; x = fabs(x); } else { /* Even */ safe_str(T("#-1 IMAGINARY NUMBER"), buff, bp); return; } } switch (n) { case 0: root = 0; break; case 1: root = x; break; case 2: root = sqrt(x); break; case 3: /* TO-DO: Configure check for cbrt()? */ default: /* Spiffy logarithm trick */ root = exp(log(x) / n); } if (sign) root = -root; safe_number(root, buff, bp); } /** Calculates the numerator and denominator for a fraction representing * a floating point number. Only works for positive numbers! * \param v the number * \param n pointer to the numerator * \param d pointer to the denominator * \param error accuracy to which the fraction should represent the original number * \return -1.0 if (v < MIN || v > MAX || error < 0.0) | (v - n/d) / v | otherwise. */ static double frac(double v, double *RESTRICT n, double *RESTRICT d, double error) { /* Based on a routine found in netlib (http://www.netlib.org) by Robert J. Craig AT&T Bell Laboratories 1200 East Naperville Road Naperville, IL 60566-7045 though I have no idea if that address is still valid. Rewritten by Raevnos to use modern C, and get rid of stupid gotos. reference: Jerome Spanier and Keith B. Oldham, "An Atlas of Functions," Springer-Verlag, 1987, pp. 665-7. */ double D, N, t; int first = 1; double epsilon, r = 0.0, m; if (v < 0 || error < 0.0) return -1.0; *d = D = 1; *n = floor(v); N = *n + 1; do { if (!first) { if (r <= 1.0) r = 1.0 / r; N += *n * floor(r); D += *d * floor(r); *n += N; *d += D; } else first = 0; r = 0.0; if (v * (*d) != *n) { r = (N - v * D) / (v * (*d) - *n); if (r <= 1.0) { t = N; N = *n; *n = t; t = D; D = *d; *d = t; } } epsilon = fabs(1.0 - *n / (v * (*d))); if (epsilon <= error) return epsilon; m = 1.0; do { m *= 10.0; } while (m * epsilon < 1.0); epsilon = 1.0 / m * (floor(0.5 + m * epsilon)); if (epsilon <= error) return epsilon; } while (r != 0.0); return epsilon; } FUNCTION(fun_fraction) { double num = 0, denom = 0; NVAL n; int sign = 0; if (!is_number(args[0])) { safe_str(T(e_num), buff, bp); return; } n = parse_number(args[0]); if (n < 0) { n = fabs(n); sign = 1; } else if (n == 0) { safe_chr('0', buff, bp); return; } frac(n, &num, &denom, 1.0e-10); if (sign) safe_chr('-', buff, bp); if (fabs(denom - 1) < 1.0e-10) safe_format(buff, bp, "%.0lf", num); else safe_format(buff, bp, "%.0lf/%.0lf", num, denom); } FUNCTION(fun_isint) { safe_boolean(is_strict_integer(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_isnum) { safe_boolean(is_strict_number(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_and) { math_and(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_or) { math_or(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_cand) { int j; char tbuf[BUFFER_LEN], *tp; char const *sp; for (j = 0; j < nargs; j++) { tp = tbuf; sp = args[j]; process_expression(tbuf, &tp, &sp, executor, caller, enactor, PE_DEFAULT, PT_DEFAULT, pe_info); *tp = '\0'; if (!parse_boolean(tbuf)) { safe_chr('0', buff, bp); return; } } safe_chr('1', buff, bp); } /* ARGSUSED */ FUNCTION(fun_cor) { int j; char tbuf[BUFFER_LEN], *tp; char const *sp; for (j = 0; j < nargs; j++) { tp = tbuf; sp = args[j]; process_expression(tbuf, &tp, &sp, executor, caller, enactor, PE_DEFAULT, PT_DEFAULT, pe_info); *tp = '\0'; if (parse_boolean(tbuf)) { safe_chr('1', buff, bp); return; } } safe_chr('0', buff, bp); } /* ARGSUSED */ FUNCTION(fun_not) { safe_boolean(!parse_boolean(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_t) { safe_boolean(parse_boolean(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_xor) { math_xor(args, nargs, buff, bp); } /** Return the spelled-out version of an integer. * \param num string containing an integer to spell out. * \param len length of num, which must be a multiple of 3, max 15. * \param buff pointer to address of output buffer. * \param bp pointer to pointer to insertion point in buff. */ static void do_spellnum(char *num, unsigned int len, char **buff, char ***bp) { static const char *bigones[] = { "", "thousand", "million", "billion", "trillion" }; static const char *singles[] = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; static const char *special[] = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; static const char *tens[] = { "", " ", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; unsigned int x0, x1, x2; int pos = len / 3; int started = 0; while (pos > 0) { pos--; x0 = num[0] - '0'; x1 = num[1] - '0'; x2 = num[2] - '0'; if (x0) { if (started) safe_chr(' ', *buff, *bp); safe_format(*buff, *bp, "%s %s", singles[x0], "hundred"); started = 1; } if (x1 == 1) { if (started) safe_chr(' ', *buff, *bp); safe_str(special[x2], *buff, *bp); started = 1; } else if (x1 || x2) { if (started) safe_chr(' ', *buff, *bp); if (x1) { safe_str(tens[x1], *buff, *bp); if (x2) safe_chr('-', *buff, *bp); } safe_str(singles[x2], *buff, *bp); started = 1; } if (pos && (x0 || x1 || x2)) { if (started) safe_chr(' ', *buff, *bp); safe_str(bigones[pos], *buff, *bp); started = 1; } num += 3; } } /* do_spellnum */ /** adds zeros to the beginning of the string, untill its length is * a multiple of 3. */ #define add_zeros(p) \ m = strlen(p) % 3; \ switch (m) { \ case 0: strcpy(num, p); break; \ case 1: num[0] = '0'; num[1] = '0'; strcpy(num + 2, p); break; \ case 2: num[0] = '0'; strcpy(num + 1, p); break; \ } \ p = num; /* ARGSUSED */ FUNCTION(fun_spellnum) { static const char *tail[] = { "", "tenth", "hundredth", "thousandth", "ten-thousandth", "hundred-thousandth", "millionth", "ten-millionth", "hundred-millionth", "billionth", "ten-billionth", "hundred-billionth", "trillionth", "ten-trillionth", "hundred-trillionth" }; char num[BUFFER_LEN]; char *number, /* the whole number (without -/+ sign and leading zeros) */ *pnumber = args[0], *pnum1, *pnum2 = NULL; /* part 1 and 2 of the number respectively */ unsigned int len, m, minus = 0, /* is the number negative? */ dot = 0, len1, len2; /* length of part 1 and 2 respectively */ pnumber = trim_space_sep(args[0], ' '); /* Is the number negative? */ if (pnumber[0] == '-') { minus = 1; pnumber++; } else if (pnumber[0] == '+') { pnumber++; } /* remove leading zeros */ while (*pnumber == '0') pnumber++; pnum1 = number = pnumber; /* Is it a number? * If so, devide the number in two parts: pnum1.pnum2 */ len = strlen(number); for (m = 0; m < len; m++) { if (*pnumber == '.') { if (dot) { safe_str(T(e_num), buff, bp); return; } dot = 1; /* allow only 1 dot in a number */ *pnumber = '\0'; /* devide the string */ pnum2 = pnumber + 1; } else if (!isdigit((unsigned char) *pnumber)) { safe_str(T(e_num), buff, bp); return; } pnumber++; } add_zeros(pnum1); len1 = strlen(pnum1); len2 = (pnum2 == NULL ? 0 : strlen(pnum2)); /* Max number is 999,999,999,999,999.999,999,999,999 */ if (len1 > 15 || len2 > 14) { safe_str(T(e_range), buff, bp); return; } /* before the . */ /* zero is special */ if (*pnum1 == '\0') { if (len2 == 0) safe_str("zero", buff, bp); } else { if (minus) safe_str("negative ", buff, bp); do_spellnum(pnum1, len1, &buff, &bp); } if (len2 > 0) { /* after the . */ /* remove leading zeros */ while (*pnum2 == '0') pnum2++; add_zeros(pnum2); pnumber = num; len = strlen(pnumber); if (len1 > 0) safe_str(" and ", buff, bp); else if (minus && len) safe_str("negative ", buff, bp); /* zero is special */ if (!len) { safe_str("zero ", buff, bp); safe_format(buff, bp, "%ss", tail[len2]); } else /* one is special too */ if (len == 3 && parse_integer(pnumber) == 1) { safe_format(buff, bp, "one %s", tail[len2]); } else { /* rest is normal */ do_spellnum(pnum1, len, &buff, &bp); safe_format(buff, bp, " %ss", tail[len2]); } } } #undef add_zeros FUNCTION(fun_bound) { if (!is_number(args[0]) || !is_number(args[1]) || (nargs == 3 && !is_number(args[2]))) { safe_str(T(e_nums), buff, bp); return; } if (parse_number(args[0]) < parse_number(args[1])) safe_strl(args[1], arglens[1], buff, bp); else if (nargs == 3 && parse_number(args[0]) > parse_number(args[2])) safe_strl(args[2], arglens[2], buff, bp); else safe_strl(args[0], arglens[0], buff, bp); } FUNCTION(fun_band) { math_band(args, nargs, buff, bp); } FUNCTION(fun_bnand) { unsigned int retval; if (!is_uinteger(args[0]) || !is_uinteger(args[1])) { safe_str(T(e_uints), buff, bp); return; } retval = parse_uinteger(args[0]) & (~parse_uinteger(args[1])); safe_uinteger(retval, buff, bp); } FUNCTION(fun_bor) { math_bor(args, nargs, buff, bp); } FUNCTION(fun_bxor) { math_bxor(args, nargs, buff, bp); } FUNCTION(fun_bnot) { if (!is_uinteger(args[0])) { safe_str(T(e_uint), buff, bp); return; } safe_uinteger(~parse_uinteger(args[0]), buff, bp); } /* ARGSUSED */ FUNCTION(fun_nand) { math_nand(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_nor) { math_nor(args, nargs, buff, bp); } /* ARGSUSED */ FUNCTION(fun_lmath) { /* lmath(<op>, <list>[, <sep>]) * is equivalant to * * &op me=<op>(%0, %1) * fold(me/op, <list>, <sep>) * * but a lot more efficient. The Tiny l-OP functions * can be simulated with @function if needed. */ int nptr; char sep; char **ptr; MATH *op; /* Allocate memory */ ptr = (char **) mush_malloc(BUFFER_LEN, "string"); if (!delim_check(buff, bp, nargs, args, 3, &sep)) { mush_free((Malloc_t) ptr, "string"); return; } nptr = list2arr(ptr, BUFFER_LEN, args[1], sep); op = math_hash_lookup(args[0]); if (!op) { safe_str(T("#-1 UNKNOWN OPERATION"), buff, bp); mush_free((Malloc_t) ptr, "string"); return; } op->func(ptr, nptr, buff, bp); mush_free((Malloc_t) ptr, "string"); } FUNCTION(fun_baseconv) { long n; int from, to; char *end; if (!(is_integer(args[1]) && is_integer(args[2]))) { safe_str(T(e_ints), buff, bp); return; } from = parse_integer(args[1]); to = parse_integer(args[2]); if (from < 2 || from > 36) { safe_str(T("#-1 FROM BASE OUT OF RANGE"), buff, bp); return; } if (to < 2 || to > 36) { safe_str(T("#-1 TO BASE OUT OF RANGE"), buff, bp); return; } n = strtol(trim_space_sep(args[0], ' '), &end, from); if (*end != '\0') { safe_str(T("#-1 MALFORMED NUMBER"), buff, bp); return; } format_long(n, buff, bp, BUFFER_LEN, to); } MATH_FUNC(math_add) { NVAL result = 0; int n; for (n = 0; n < nptr; n++) { if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } result += parse_number(ptr[n]); } safe_number(result, buff, bp); } MATH_FUNC(math_and) { int n; for (n = 0; n < nptr; n++) { if (!parse_boolean(ptr[n])) { safe_chr('0', buff, bp); return; } } safe_chr('1', buff, bp); return; } MATH_FUNC(math_sub) { /* Subtraction */ NVAL result; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } result = parse_number(ptr[0]); for (n = 1; n < nptr; n++) { if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } result -= parse_number(ptr[n]); } safe_number(result, buff, bp); } MATH_FUNC(math_mul) { NVAL result; int n; /* Multiplication */ if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } result = parse_number(ptr[0]); for (n = 1; n < nptr; n++) { if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } result *= parse_number(ptr[n]); } safe_number(result, buff, bp); } MATH_FUNC(math_min) { NVAL result; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } result = parse_number(ptr[0]); for (n = 1; n < nptr; n++) { NVAL test; if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } test = parse_number(ptr[n]); result = (result > test) ? test : result; } safe_number(result, buff, bp); } MATH_FUNC(math_max) { NVAL result; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } result = parse_number(ptr[0]); for (n = 1; n < nptr; n++) { NVAL test; if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } test = parse_number(ptr[n]); result = (result > test) ? result : test; } safe_number(result, buff, bp); } MATH_FUNC(math_mean) { NVAL result = 0, count = 0; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } for (n = 0; n < nptr; n++) { if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } result += parse_number(ptr[n]); count++; } safe_number(result / count, buff, bp); } MATH_FUNC(math_div) { /* Division, truncating to match remainder */ int divresult, n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_integer(ptr[0])) { safe_str(T(e_ints), buff, bp); return; } divresult = parse_integer(ptr[0]); for (n = 1; n < nptr; n++) { int temp; if (!is_integer(ptr[n])) { safe_str(T(e_ints), buff, bp); return; } temp = parse_integer(ptr[n]); if (temp == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } if (divresult < 0) { if (temp < 0) divresult = -divresult / -temp; else divresult = -(-divresult / temp); } else { if (temp < 0) divresult = -(divresult / -temp); else divresult = divresult / temp; } } safe_integer(divresult, buff, bp); } MATH_FUNC(math_floordiv) { /* Division taking the floor, to match modulo */ int divresult, n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_integer(ptr[0])) { safe_str(T(e_ints), buff, bp); return; } divresult = parse_integer(ptr[0]); for (n = 1; n < nptr; n++) { int temp; if (!is_integer(ptr[n])) { safe_str(T(e_ints), buff, bp); return; } temp = parse_integer(ptr[n]); if (temp == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } if (divresult < 0) { if (temp < 0) divresult = -divresult / -temp; else divresult = -((-divresult + temp - 1) / temp); } else { if (temp < 0) divresult = -((divresult - temp - 1) / -temp); else divresult = divresult / temp; } } safe_integer(divresult, buff, bp); } MATH_FUNC(math_fdiv) { NVAL result; int n; /* Floating-point division */ if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } result = parse_number(ptr[0]); for (n = 1; n < nptr; n++) { NVAL temp; if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } temp = parse_number(ptr[n]); if (temp == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } result /= temp; } safe_number(result, buff, bp); } MATH_FUNC(math_modulo) { /* Modulo */ int divresult, n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_integer(ptr[0])) { safe_str(T(e_ints), buff, bp); return; } divresult = parse_integer(ptr[0]); for (n = 1; n < nptr; n++) { int temp; if (!is_integer(ptr[n])) { safe_str(T(e_ints), buff, bp); return; } temp = parse_integer(ptr[n]); if (temp == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } if (divresult < 0) { if (temp < 0) divresult = -(-divresult % -temp); else divresult = (temp - (-divresult % temp)) % temp; } else { if (temp < 0) divresult = -((-temp - (divresult % -temp)) % -temp); else divresult = divresult % temp; } } safe_integer(divresult, buff, bp); } MATH_FUNC(math_remainder) { /* Remainder */ int divresult, n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_integer(ptr[0])) { safe_str(T(e_ints), buff, bp); return; } divresult = parse_integer(ptr[0]); for (n = 1; n < nptr; n++) { int temp; if (!is_integer(ptr[n])) { safe_str(T(e_ints), buff, bp); return; } temp = parse_integer(ptr[n]); if (temp == 0) { safe_str(T("#-1 DIVISION BY ZERO"), buff, bp); return; } if (divresult < 0) { if (temp < 0) divresult = -(-divresult % -temp); else divresult = -(-divresult % temp); } else { if (temp < 0) divresult = divresult % -temp; else divresult = divresult % temp; } } safe_integer(divresult, buff, bp); } MATH_FUNC(math_band) { unsigned int bretval; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_uinteger(ptr[0])) { safe_str(T(e_uints), buff, bp); return; } bretval = parse_uinteger(ptr[0]); for (n = 1; n < nptr; n++) { if (!is_uinteger(ptr[n])) { safe_str(T(e_uints), buff, bp); return; } bretval &= parse_uinteger(ptr[n]); } safe_uinteger(bretval, buff, bp); } MATH_FUNC(math_bor) { unsigned int bretval; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_uinteger(ptr[0])) { safe_str(T(e_uints), buff, bp); return; } bretval = parse_uinteger(ptr[0]); for (n = 1; n < nptr; n++) { if (!is_uinteger(ptr[n])) { safe_str(T(e_uints), buff, bp); return; } bretval |= parse_uinteger(ptr[n]); } safe_uinteger(bretval, buff, bp); } MATH_FUNC(math_bxor) { unsigned int bretval; int n; if (nptr < 1) { safe_chr('0', buff, bp); return; } if (!is_uinteger(ptr[0])) { safe_str(T(e_uints), buff, bp); return; } bretval = parse_uinteger(ptr[0]); for (n = 1; n < nptr; n++) { if (!is_uinteger(ptr[n])) { safe_str(T(e_uints), buff, bp); return; } bretval ^= parse_uinteger(ptr[n]); } safe_uinteger(bretval, buff, bp); } MATH_FUNC(math_or) { int n; /* Or */ for (n = 0; n < nptr; n++) { if (parse_boolean(ptr[n])) { safe_chr('1', buff, bp); return; } } safe_chr('0', buff, bp); } MATH_FUNC(math_nor) { int n; /* nor */ for (n = 0; n < nptr; n++) { if (parse_boolean(ptr[n])) { safe_chr('0', buff, bp); return; } } safe_chr('1', buff, bp); } MATH_FUNC(math_nand) { int n; for (n = 0; n < nptr; n++) { if (!parse_boolean(ptr[n])) { safe_chr('1', buff, bp); return; } } safe_chr('0', buff, bp); } MATH_FUNC(math_xor) { int found = 0, n; for (n = 0; n < nptr; n++) { if (parse_boolean(ptr[n])) { if (found == 0) { found = 1; } else { safe_chr('0', buff, bp); return; } } } if (found) safe_chr('1', buff, bp); else safe_chr('0', buff, bp); } static int nval_sort(const void *a, const void *b) { const NVAL *x = a, *y = b; const double epsilon = pow(10, -FLOAT_PRECISION); int eq = (fabs(*x - *y) <= (epsilon * fabs(*x))); return eq ? 0 : (*x > *y ? 1 : -1); } static NVAL find_median(NVAL *numbers, int nargs) { if (nargs == 0) return 0; if (nargs == 1) return numbers[0]; qsort(numbers, nargs, sizeof(NVAL), nval_sort); if ((nargs % 2) == 1) /* Odd # of items */ return numbers[nargs / 2]; else return (numbers[(nargs / 2) - 1] + numbers[nargs / 2]) / (NVAL) 2; } FUNCTION(fun_median) { math_median(args, nargs, buff, bp); } FUNCTION(fun_mean) { math_mean(args, nargs, buff, bp); } FUNCTION(fun_stddev) { math_stddev(args, nargs, buff, bp); } /* ARGSUSED */ MATH_FUNC(math_median) { NVAL median; NVAL *numbers; int n; numbers = mush_malloc(nptr * sizeof(NVAL), "number_array"); for (n = 0; n < nptr; n++) { if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); mush_free(numbers, "number_array"); return; } numbers[n] = parse_number(ptr[n]); } median = find_median(numbers, nptr); mush_free(numbers, "number_array"); safe_number(median, buff, bp); } MATH_FUNC(math_stddev) { NVAL m, om, s, os, v; int n; if (nptr < 2) { safe_number(0, buff, bp); return; } if (!is_number(ptr[0])) { safe_str(T(e_nums), buff, bp); return; } m = parse_number(ptr[0]); s = 0; for (n = 1; n < nptr; n++) { om = m; os = s; if (!is_number(ptr[n])) { safe_str(T(e_nums), buff, bp); return; } v = parse_number(ptr[n]); m = om + (v - om) / (n + 1); s = os + (v - om) * (v - m); } safe_number(sqrt(s / (nptr - 1)), buff, bp); } /** A list of MATH_FUNCs that are suitable for lmath() */ MATH mlist[] = { {"ADD", math_add} , {"SUB", math_sub} , {"MUL", math_mul} , {"DIV", math_div} , {"FLOORDIV", math_floordiv} , {"MOD", math_modulo} , {"MODULO", math_modulo} , {"MODULUS", math_modulo} , {"REMAINDER", math_remainder} , {"MIN", math_min} , {"MAX", math_max} , {"AND", math_and} , {"NAND", math_nand} , {"OR", math_or} , {"NOR", math_nor} , {"XOR", math_xor} , {"BAND", math_band} , {"BOR", math_bor} , {"BXOR", math_bxor} , {"FDIV", math_fdiv} , {"MEAN", math_mean} , {"MEDIAN", math_median} , {"STDDEV", math_stddev} , {NULL, NULL} }; static MATH * math_hash_lookup(char *name) { return (MATH *) hashfind(strupper(name), &htab_math); } static void math_hash_insert(const char *name, MATH *func) { hashadd(name, (void *) func, &htab_math); } /** Initialize the math function hash table. */ void init_math_hashtab(void) { MATH *fp; hashinit(&htab_math, 32, sizeof(MATH)); for (fp = mlist; fp->name; fp++) math_hash_insert(fp->name, (MATH *) fp); } static NVAL angle_to_rad(NVAL angle, const char *from) { if (!from) return angle; switch (*from) { case 'r': case 'R': return angle; case 'd': case 'D': return angle * (M_PI / 180.0); case 'g': case 'G': return angle * (M_PI / 200.0); default: return angle; } } static NVAL rad_to_angle(NVAL angle, const char *to) { if (!to) return angle; switch (*to) { case 'r': case 'R': return angle; case 'd': case 'D': return angle * (180.0 / M_PI); case 'g': case 'G': return angle * (200.0 / M_PI); default: return angle; } }