From a87b2af2cf9f25c0d8b199a12b72ff17d2f3b460 Mon Sep 17 00:00:00 2001 From: Stephane Alnet Date: Sat, 30 Nov 2013 01:03:53 +0100 Subject: [PATCH 1/4] Mathops extensions Add a new `math_rpn` function, and capacity to add new operators when using the RPN parser. New operators (RPN only): `exp`, `floor` --- modules/mathops/doc/mathops.xml | 7 + modules/mathops/doc/mathops_admin.xml | 67 ++++++ modules/mathops/math_funcs.c | 286 ++++++++++++++++++++++---- modules/mathops/math_funcs.h | 8 +- modules/mathops/mathops.c | 21 +- 5 files changed, 340 insertions(+), 49 deletions(-) diff --git a/modules/mathops/doc/mathops.xml b/modules/mathops/doc/mathops.xml index 06efbf9a926..822c21adc7d 100644 --- a/modules/mathops/doc/mathops.xml +++ b/modules/mathops/doc/mathops.xml @@ -28,6 +28,13 @@ liviu@opensips.org + + Stephane + Alnet +
+ stephane@shimaore.net +
+
2013 diff --git a/modules/mathops/doc/mathops_admin.xml b/modules/mathops/doc/mathops_admin.xml index 6d38758f03f..0a068f7c95f 100644 --- a/modules/mathops/doc/mathops_admin.xml +++ b/modules/mathops/doc/mathops_admin.xml @@ -125,6 +125,73 @@ if (math_eval("$avp(1) * ($avp(3) - ($avp(1) - $avp(2))) / $avp(3)", "$avp(resul +
+ + <function moreinfo="none">math_rpn(expression, result_pvar)</function> + + + The function evaluates a given RPN expression and writes the results in the + output pseudo-variable. The expression may contain any number of pseudo + variables. + + + The expression is specified as a Reverse Polish Notation script. Values are pushed + onto a stack, while operations are executed on that stack. The following operations + are supported: + + + binary operators: + - / * mod pow + + + unary functions: neg exp ln log10 abs sqrt cbrt floor ceil round nearbyint trunc + neg will change the sign of the top of the stack + ln is natural logarithm; abs is absolute value; other functions are standard C functions + + + constants: e pi + + + stack manipulations commands: drop dup swap + + + + Meaning of the parameters is as follows: + + + expression - String containing a + RPN expression. It can also include pseudo variables. The + expression parameter can only be given as a string. + + + + result_pvar - pseudo-variable which will + hold the result of the evaluation. Specified as a quoted string. + + + + + This function can be used from any route. + + + <function moreinfo="none">math_rpn</function> usage + +$avp(1) = "3"; + +if (math_rpn("1 $avp(1) swap swap dup drop / exp ln 1 swap /", "$avp(result)")) { + xlog("Result of expression: $avp(result)\n"); +} else { + xlog("RPN eval failed!\n"); +} + +/* This example RPN script will push 1 then 3 onto the stack, then do a couple no-ops +(exchange the two values twice, duplicate one of them then drop the duplicate), +compute the division of 1 by 3, then do another no-op (expnentiation then logarithm), and +finally compute 1 divided by the result, giving 3 as the result. */ + + +
+ +
<function moreinfo="none">math_trunc(number, result_pvar)</function> diff --git a/modules/mathops/math_funcs.c b/modules/mathops/math_funcs.c index 2ad393e869a..cc7629f9865 100644 --- a/modules/mathops/math_funcs.c +++ b/modules/mathops/math_funcs.c @@ -94,24 +94,49 @@ static int get_op(char symbol) } } -static void push_op(int type) +static int push_op(int type) { + if(top >= MAX_STACK_SIZE) { + LM_ERR("RPN Stack Full\n"); + return -1; + } + stack[top].type = type; top++; + return 0; } -static void push_number(int type, double value) +static int push_number(double value) { - stack[top].type = type; + if(top >= MAX_STACK_SIZE) { + LM_ERR("RPN Stack Full\n"); + return -1; + } + + LM_DBG("push %f\n",value); + stack[top].type = MATHOP_NUMBER; stack[top].value = value; top++; + return 0; } -static double pop_number(void) +static int pop_number(double *value) { + if(top <= 0) { + LM_ERR("RPN Stack Empty\n"); + return -1; + } + top--; - return stack[top].value; + if(stack[top].type != MATHOP_NUMBER) { + LM_ERR("RPN Stack Top is not a number\n"); + return -1; + } + + *value = stack[top].value; + LM_DBG("pop = %f\n",*value); + return 0; } static void pop_to_output(void) @@ -128,29 +153,88 @@ static void pop_while_higher(int op_type) } } -static double pop_and_eval(int op_type) +static int rpn_eval(const token* t) { - double o1, o2; + double o1, o2; - o2 = pop_number(); - o1 = pop_number(); + switch (t->type) { + case MATHOP_NUMBER: + return push_number(t->value); - switch (op_type) { - case MATHOP_ADD: - return o1 + o2; - - case MATHOP_SUB: - return o1 - o2; - - case MATHOP_MUL: - return o1 * o2; - - case MATHOP_DIV: - return o1 / o2; - - default: - return 0; - } + case MATHOP_ADD: + return pop_number(&o2) || pop_number(&o1) || push_number(o1 + o2); + + case MATHOP_SUB: + return pop_number(&o2) || pop_number(&o1) || push_number(o1 - o2); + + case MATHOP_MUL: + return pop_number(&o2) || pop_number(&o1) || push_number(o1 * o2); + + case MATHOP_DIV: + return pop_number(&o2) || pop_number(&o1) || push_number(o1 / o2); + + case MATHOP_NEG: + return pop_number(&o1) || push_number(-o1); + + case MATHOP_DROP: + return pop_number(&o1); + + case MATHOP_DUP: + if(pop_number(&o1)) return -1; + return push_number(o1) || push_number(o1); + + case MATHOP_SWAP: + return pop_number(&o2) || pop_number(&o1) || push_number(o2) || push_number(o1); + + case MATHOP_MOD: + return pop_number(&o2) || pop_number(&o1) || push_number(fmod(o1,o2)); + + case MATHOP_POW: + return pop_number(&o2) || pop_number(&o1) || push_number(pow(o1,o2)); + + case MATHOP_EXP: + return pop_number(&o1) || push_number(exp(o1)); + + case MATHOP_LOG10: + return pop_number(&o1) || push_number(log10(o1)); + + case MATHOP_LN: + return pop_number(&o1) || push_number(log(o1)); + + case MATHOP_ABS: + return pop_number(&o1) || push_number(fabs(o1)); + + case MATHOP_SQRT: + return pop_number(&o1) || push_number(sqrt(o1)); + + case MATHOP_CBRT: + return pop_number(&o1) || push_number(cbrt(o1)); + + case MATHOP_FLOOR: + return pop_number(&o1) || push_number(floor(o1)); + + case MATHOP_CEIL: + return pop_number(&o1) || push_number(ceil(o1)); + + case MATHOP_ROUND: + return pop_number(&o1) || push_number(round(o1)); + + case MATHOP_NEARBYINT: + return pop_number(&o1) || push_number(nearbyint(o1)); + + case MATHOP_TRUNC: + return pop_number(&o1) || push_number(trunc(o1)); + + case MATHOP_E: + return push_number(M_E); + + case MATHOP_PI: + return push_number(M_PI); + + default: + LM_WARN("Invalid RPN token type\n"); + return -1; + } } #define inc_and_trim(s) \ @@ -160,6 +244,79 @@ static double pop_and_eval(int op_type) trim_leading(&s); \ } while (0) +static inline void parse_word(str* _s, str* word) +{ + trim_leading(_s); + word->len = 0; + word->s = _s->s; + for(; _s->len > 0; _s->len--, _s->s++) { + switch(*(_s->s)) { + case ' ': + case '\t': + case '\r': + case '\n': + return; + + default: + word->len++; + break; + } + } +} + +struct mathop_entry { + str s; + int op; +}; + +const struct mathop_entry word_to_mathop[] = { + {s:{ len:1, s:"+" }, op:MATHOP_ADD}, + {s:{ len:1, s:"-" }, op:MATHOP_SUB}, + {s:{ len:1, s:"*" }, op:MATHOP_MUL}, + {s:{ len:1, s:"/" }, op:MATHOP_DIV}, + {s:{ len:4, s:"drop" }, op:MATHOP_DROP}, + {s:{ len:3, s:"dup" }, op:MATHOP_DUP}, + {s:{ len:4, s:"swap" }, op:MATHOP_SWAP}, + {s:{ len:3, s:"mod" }, op:MATHOP_MOD}, + {s:{ len:3, s:"pow" }, op:MATHOP_POW}, + {s:{ len:3, s:"exp" }, op:MATHOP_EXP}, + {s:{ len:2, s:"ln" }, op:MATHOP_LN}, + {s:{ len:3, s:"log10" }, op:MATHOP_LOG10}, + {s:{ len:3, s:"abs" }, op:MATHOP_ABS}, + {s:{ len:3, s:"neg" }, op:MATHOP_NEG}, + {s:{ len:4, s:"sqrt" }, op:MATHOP_SQRT}, + {s:{ len:4, s:"cbrt" }, op:MATHOP_CBRT}, + {s:{ len:5, s:"floor" }, op:MATHOP_FLOOR}, + {s:{ len:4, s:"ceil" }, op:MATHOP_CEIL}, + {s:{ len:5, s:"round" }, op:MATHOP_ROUND}, + {s:{ len:9, s:"nearbyint" }, op:MATHOP_NEARBYINT}, + {s:{ len:5, s:"trunc" }, op:MATHOP_TRUNC}, + {s:{ len:1, s:"e" }, op:MATHOP_E}, + {s:{ len:1, s:"pi" }, op:MATHOP_PI}, + {s:{ len:0, s:NULL}, op:-1} +}; + +static int get_rpn_op(str *_s) +{ + str word; + const struct mathop_entry* j; + + trim_leading(_s); + parse_word(_s,&word); + if(word.len == 0) { + return -1; + } + + for( j = word_to_mathop; j->s.len > 0; j++ ) { + if(j->s.len == word.len && !strncmp(j->s.s,word.s,j->s.len)) { + return j->op; + } + } + + LM_WARN("Parse expr error: Invalid operator! <%.*s>\n", word.len, word.s); + return -1; +} + /** * Shunting-yard algorithm * @@ -245,6 +402,49 @@ static int convert_to_rpn(str *exp) return 0; } +static int parse_rpn(str *exp) +{ + double d; + char *p; + int op; + str s; + + p = exp->s; + s.s = exp->s; + s.len = exp->len; + + while (s.len) { + + if (*s.s >= '0' && *s.s <= '9') { + errno = 0; + d = strtod(s.s, &p); + + s.len -= p - s.s; + s.s = p; + + if (errno == ERANGE) { + LM_WARN("Overflow in parsing a numeric value!\n"); + return -1; + } + + output[pos].type = MATHOP_NUMBER; + output[pos].value = d; + pos++; + } else { + op = get_rpn_op(&s); + if (op < 0) { + return -1; + } + + output[pos].type = op; + pos++; + } + trim_leading(&s); + } + + return 0; +} + /** * The function assumes that the 'output' buffer is properly written and * the 'pos' variable holds the size of the buffer @@ -252,37 +452,26 @@ static int convert_to_rpn(str *exp) static int evaluate_rpn_output(double *result) { int i; - double val; - /* since all supported operators are binary, just hardcode the 2 */ for (i = 0; i < pos; i++) { - - if (output[i].type == MATHOP_NUMBER) { - push_number(MATHOP_NUMBER, output[i].value); - } else if (top >= 2) { - val = pop_and_eval(output[i].type); - push_number(MATHOP_NUMBER, val); - - } else { - LM_ERR("Parse expr error: insufficient operands!\n"); - return -1; + if(rpn_eval(output+i) < 0) { + return -1; } } - if (top > 1) { - LM_ERR("Parse expr error: insufficient operators/closing parantheses!\n"); + if (top != 1) { + LM_ERR("Parse expr error: stack has %d elements\n",top); return -1; } - *result = stack[top-1].value; - return 0; + return pop_number(result); } /** * Computes the result of a given expression */ -int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result_var) +int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result_var, short is_rpn) { double result; pv_value_t pv_val; @@ -293,9 +482,16 @@ int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result_var) top = 0; pos = 0; - if (convert_to_rpn(exp) != 0) { - LM_ERR("Failed to convert expression to RPN form!\n"); - return -1; + if(is_rpn) { + if (parse_rpn(exp) != 0) { + LM_ERR("Failed to parse RPN!\n"); + return -1; + } + } else { + if (convert_to_rpn(exp) != 0) { + LM_ERR("Failed to convert expression to RPN form!\n"); + return -1; + } } if (evaluate_rpn_output(&result) != 0) { diff --git a/modules/mathops/math_funcs.h b/modules/mathops/math_funcs.h index 6c1fab2fb54..b4cc35b3a85 100644 --- a/modules/mathops/math_funcs.h +++ b/modules/mathops/math_funcs.h @@ -44,7 +44,10 @@ extern int decimal_digits; enum { MATHOP_NUMBER = 0, MATHOP_LPAREN, MATHOP_ADD, MATHOP_SUB, MATHOP_MUL, - MATHOP_DIV }; + MATHOP_DIV, MATHOP_DROP, MATHOP_DUP, MATHOP_SWAP, MATHOP_MOD, MATHOP_NEG, + MATHOP_POW, MATHOP_EXP, MATHOP_LN, MATHOP_LOG10, MATHOP_ABS, MATHOP_SQRT, + MATHOP_CBRT, MATHOP_FLOOR, MATHOP_CEIL, MATHOP_ROUND, MATHOP_NEARBYINT, + MATHOP_TRUNC, MATHOP_E, MATHOP_PI }; typedef struct _token { int type; @@ -59,7 +62,6 @@ int basic_round_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int round_dp_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits); int round_sf_op(struct sip_msg *msg, str *n, pv_spec_p result_var, int digits); -int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result); +int evaluate_exp(struct sip_msg *msg, str *exp, pv_spec_p result, short is_rpn); #endif /* __MATHOPS_H__ */ - diff --git a/modules/mathops/mathops.c b/modules/mathops/mathops.c index 3a9ef968415..7cf66f5876d 100644 --- a/modules/mathops/mathops.c +++ b/modules/mathops/mathops.c @@ -69,6 +69,7 @@ static int fixup_round_op(void **param, int param_no); * Function headers */ static int w_evaluate_exp(struct sip_msg *msg, char *exp, char *result); +static int w_evaluate_rpn(struct sip_msg *msg, char *exp, char *result); static int w_basic_round_op(struct sip_msg *msg, char *number, char *result, double (*math_op)(double)); static int w_floor_op(struct sip_msg *msg, char *number, char *result); @@ -87,6 +88,9 @@ static cmd_export_t cmds[] = { {"math_eval",(cmd_function)w_evaluate_exp, 2, fixup_evaluate_exp, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, + {"math_rpn",(cmd_function)w_evaluate_rpn, 2, fixup_evaluate_exp, 0, + REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| + STARTUP_ROUTE|TIMER_ROUTE}, {"math_floor",(cmd_function)w_floor_op, 2, fixup_binary_op, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE}, @@ -257,7 +261,22 @@ static int w_evaluate_exp(struct sip_msg *msg, char *exp, char *result) LM_DBG("Evaluating expression: %.*s\n", s.len, s.s); - return evaluate_exp(msg, &s, (pv_spec_p)result); + return evaluate_exp(msg, &s, (pv_spec_p)result, 0); +} + +static int w_evaluate_rpn(struct sip_msg *msg, char *exp, char *result) +{ + pv_elem_p exp_fmt = (pv_elem_p)exp; + str s; + + if (pv_printf_s(msg, exp_fmt, &s) != 0) { + LM_ERR("Failed to print the pv format string!\n"); + return -1; + } + + LM_DBG("Evaluating expression: %.*s\n", s.len, s.s); + + return evaluate_exp(msg, &s, (pv_spec_p)result, 1); } From be2ce6f82236c4a8fd0d51b0dd7549b137d37071 Mon Sep 17 00:00:00 2001 From: Stephane Alnet <stephane@shimaore.net> Date: Sat, 30 Nov 2013 01:04:11 +0100 Subject: [PATCH 2/4] Built README --- modules/mathops/README | 91 ++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/modules/mathops/README b/modules/mathops/README index 38118777ca4..559e1e8f3ef 100644 --- a/modules/mathops/README +++ b/modules/mathops/README @@ -9,6 +9,10 @@ Edited by Liviu Chircu +Edited by + +Stephane Alnet + Copyright © 2013 www.opensips-solutions.com Revision History Revision $Revision$ $Date$ @@ -31,21 +35,23 @@ Liviu Chircu 1.4. Exported Functions 1.4.1. math_eval(expression, result_pvar) - 1.4.2. math_trunc(number, result_pvar) - 1.4.3. math_floor(number, result_pvar) - 1.4.4. math_ceil(number, result_pvar) - 1.4.5. math_round(number, result_pvar[, decimals]) - 1.4.6. math_round_sf(number, result_pvar, figures) + 1.4.2. math_rpn(expression, result_pvar) + 1.4.3. math_trunc(number, result_pvar) + 1.4.4. math_floor(number, result_pvar) + 1.4.5. math_ceil(number, result_pvar) + 1.4.6. math_round(number, result_pvar[, decimals]) + 1.4.7. math_round_sf(number, result_pvar, figures) List of Examples 1.1. Setting the decimal_digits module parameter 1.2. math_eval usage - 1.3. math_trunc usage - 1.4. math_floor usage - 1.5. math_ceil usage - 1.6. math_round usage - 1.7. math_round_sf usage + 1.3. math_rpn usage + 1.4. math_trunc usage + 1.5. math_floor usage + 1.6. math_ceil usage + 1.7. math_round usage + 1.8. math_round_sf usage Chapter 1. Admin Guide @@ -118,7 +124,52 @@ vp(result)")) { ... -1.4.2. math_trunc(number, result_pvar) +1.4.2. math_rpn(expression, result_pvar) + + The function evaluates a given RPN expression and writes the + results in the output pseudo-variable. The expression may + contain any number of pseudo variables. + + The expression is specified as a Reverse Polish Notation + script. Values are pushed onto a stack, while operations are + executed on that stack. The following operations are supported: + * binary operators: + - / * mod pow + * unary functions: neg exp ln log10 abs sqrt cbrt floor ceil + round nearbyint trunc + neg will change the sign of the top of the stack + ln is natural logarithm; abs is absolute value; other + functions are standard C functions + * constants: e pi + * stack manipulations commands: drop dup swap + + Meaning of the parameters is as follows: + * expression - String containing a RPN expression. It can + also include pseudo variables. The expression parameter can + only be given as a string. + * result_pvar - pseudo-variable which will hold the result of + the evaluation. Specified as a quoted string. + + This function can be used from any route. + + Example 1.3. math_rpn usage +$avp(1) = "3"; + +if (math_rpn("1 $avp(1) swap swap dup drop / exp ln 1 swap /", "$avp(res +ult)")) { + xlog("Result of expression: $avp(result)\n"); +} else { + xlog("RPN eval failed!\n"); +} + +/* This example RPN script will push 1 then 3 onto the stack, then do a +couple no-ops +(exchange the two values twice, duplicate one of them then drop the dupl +icate), +compute the division of 1 by 3, then do another no-op (expnentiation the +n logarithm), and +finally compute 1 divided by the result, giving 3 as the result. */ + +1.4.3. math_trunc(number, result_pvar) Truncation of a number towards zero. This means that trunc(3.7) = 3.0 and trunc(-2.9) = -2.0. @@ -134,7 +185,7 @@ vp(result)")) { This function can be used from any route. - Example 1.3. math_trunc usage + Example 1.4. math_trunc usage ... # Truncate a random number @@ -147,7 +198,7 @@ if (math_trunc("$avp(1)", "$avp(result)")) { } ... -1.4.3. math_floor(number, result_pvar) +1.4.4. math_floor(number, result_pvar) Truncates a number, always towards -infinity. This means that floor(3.7) = 3.0 and floor(-2.9) = -3.0 @@ -163,7 +214,7 @@ if (math_trunc("$avp(1)", "$avp(result)")) { This function can be used from any route. - Example 1.4. math_floor usage + Example 1.5. math_floor usage ... # Truncate a random number @@ -176,7 +227,7 @@ if (math_floor("$avp(1)", "$avp(result)")) { } ... -1.4.4. math_ceil(number, result_pvar) +1.4.5. math_ceil(number, result_pvar) Truncates a number, always towards +infinity. This means that ceil(3.2) = 4.0 and ceil(-2.9) = -2.0 @@ -192,7 +243,7 @@ if (math_floor("$avp(1)", "$avp(result)")) { This function can be used from any route. - Example 1.5. math_ceil usage + Example 1.6. math_ceil usage ... # Truncate a random number @@ -205,7 +256,7 @@ if (math_ceil("$avp(1)", "$avp(result)")) { } ... -1.4.5. math_round(number, result_pvar[, decimals]) +1.4.6. math_round(number, result_pvar[, decimals]) The round function returns the nearest integer, and tie-breaking is done away from zero. Examples: round(1.2) = @@ -230,7 +281,7 @@ if (math_ceil("$avp(1)", "$avp(result)")) { This function can be used from any route. - Example 1.6. math_round usage + Example 1.7. math_round usage ... # Rounding PI @@ -255,7 +306,7 @@ if (math_round("$avp(1)", "$avp(result)", "4")) { } ... -1.4.6. math_round_sf(number, result_pvar, figures) +1.4.7. math_round_sf(number, result_pvar, figures) To give a simple explanation, rounding to N significant figures is done by first obtaining the number resulted from keeping N @@ -284,7 +335,7 @@ if (math_round("$avp(1)", "$avp(result)", "4")) { This function can be used from any route. - Example 1.7. math_round_sf usage + Example 1.8. math_round_sf usage ... # Rounding PI From d8239329773c8bd01b22887550a70a612f1f7a94 Mon Sep 17 00:00:00 2001 From: Stephane Alnet <stephane@shimaore.net> Date: Sat, 30 Nov 2013 01:16:32 +0100 Subject: [PATCH 3/4] Corrected typos. --- modules/mathops/doc/mathops_admin.xml | 2 +- modules/mathops/math_funcs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mathops/doc/mathops_admin.xml b/modules/mathops/doc/mathops_admin.xml index 0a068f7c95f..515835e6501 100644 --- a/modules/mathops/doc/mathops_admin.xml +++ b/modules/mathops/doc/mathops_admin.xml @@ -185,7 +185,7 @@ if (math_rpn("1 $avp(1) swap swap dup drop / exp ln 1 swap /", "$avp(result)")) /* This example RPN script will push 1 then 3 onto the stack, then do a couple no-ops (exchange the two values twice, duplicate one of them then drop the duplicate), -compute the division of 1 by 3, then do another no-op (expnentiation then logarithm), and +compute the division of 1 by 3, then do another no-op (exponentiation then logarithm), and finally compute 1 divided by the result, giving 3 as the result. */ </programlisting> </example> diff --git a/modules/mathops/math_funcs.c b/modules/mathops/math_funcs.c index cc7629f9865..32c17278d4c 100644 --- a/modules/mathops/math_funcs.c +++ b/modules/mathops/math_funcs.c @@ -292,7 +292,7 @@ const struct mathop_entry word_to_mathop[] = { {s:{ len:9, s:"nearbyint" }, op:MATHOP_NEARBYINT}, {s:{ len:5, s:"trunc" }, op:MATHOP_TRUNC}, {s:{ len:1, s:"e" }, op:MATHOP_E}, - {s:{ len:1, s:"pi" }, op:MATHOP_PI}, + {s:{ len:2, s:"pi" }, op:MATHOP_PI}, {s:{ len:0, s:NULL}, op:-1} }; From 277615685ce63aa171807ce560d51eb6d5f4c262 Mon Sep 17 00:00:00 2001 From: Stephane Alnet <stephane@shimaore.net> Date: Sat, 30 Nov 2013 01:17:21 +0100 Subject: [PATCH 4/4] Rebuilt README --- modules/mathops/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mathops/README b/modules/mathops/README index 559e1e8f3ef..5c8c57c3312 100644 --- a/modules/mathops/README +++ b/modules/mathops/README @@ -165,8 +165,8 @@ ult)")) { couple no-ops (exchange the two values twice, duplicate one of them then drop the dupl icate), -compute the division of 1 by 3, then do another no-op (expnentiation the -n logarithm), and +compute the division of 1 by 3, then do another no-op (exponentiation th +en logarithm), and finally compute 1 divided by the result, giving 3 as the result. */ 1.4.3. math_trunc(number, result_pvar)