From baa1feb6d2367ad398bb61fcdc4e2bbcafc8e24d Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Sun, 4 May 2014 07:49:15 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 183 ++++++++++++++++++++++++++++++++--------------------- readme.txt | 60 ++++++++++-------- 2 files changed, 147 insertions(+), 96 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 34e43fb..32f3307 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -1,32 +1,32 @@ /* - ******************************************************************* - * C++ Mathematical Expression Toolkit Library * - * * - * Author: Arash Partow (1999-2014) * - * URL: http://www.partow.net/programming/exprtk/index.html * - * * - * Copyright notice: * - * Free use of the C++ Mathematical Expression Toolkit Library is * - * permitted under the guidelines and in accordance with the most * - * current version of the Common Public License. * - * http://www.opensource.org/licenses/cpl1.0.php * - * * - * Example expressions: * - * (00) (y + x / y) * (x - y / x) * - * (01) (x^2 / sin(2 * pi / y)) - x / 2 * - * (02) sqrt(1 - (x^2)) * - * (03) 1 - sin(2 * x) + cos(pi / y) * - * (04) a * exp(2 * t) + c * - * (05) if(((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) * - * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x * - * (07) z := x + sin(2 * pi / y) * - * (08) u := 2 * (pi * z) / (w := x + cos(y / pi)) * - * (09) clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1) * - * (10) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0) * - * (11) (2sin(x)cos(2y)7 + 1) == (2 * sin(x) * cos(2*y) * 7 + 1) * - * (12) (x ilike 's*ri?g') and [y < (3 z^7 + w)] * - * * - ******************************************************************* + ****************************************************************** + * C++ Mathematical Expression Toolkit Library * + * * + * Author: Arash Partow (1999-2014) * + * URL: http://www.partow.net/programming/exprtk/index.html * + * * + * Copyright notice: * + * Free use of the C++ Mathematical Expression Toolkit Library is * + * permitted under the guidelines and in accordance with the most * + * current version of the Common Public License. * + * http://www.opensource.org/licenses/cpl1.0.php * + * * + * Example expressions: * + * (00) (y + x / y) * (x - y / x) * + * (01) (x^2 / sin(2 * pi / y)) - x / 2 * + * (02) sqrt(1 - (x^2)) * + * (03) 1 - sin(2 * x) + cos(pi / y) * + * (04) a * exp(2 * t) + c * + * (05) if(((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) * + * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x * + * (07) z := x + sin(2 * pi / y) * + * (08) u := 2 * (pi * z) / (w := x + cos(y / pi)) * + * (09) clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1) * + * (10) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0) * + * (11) (2sin(x)cos(2y)7 + 1) == (2 * sin(x) * cos(2*y) * 7 + 1) * + * (12) (x ilike 's*ri?g') and [y < (3 z^7 + w)] * + * * + ****************************************************************** */ @@ -288,8 +288,9 @@ namespace exprtk "for", "frac", "grad2deg", "hypot", "iclamp", "if", "else", "ilike", "in", "inrange", "like", "log", "log10", "log2", "logn", "log1p", "mand", "max", "min", "mod", "mor", "mul", "nand", "nor", "not", "not_equal", "null", "or", "pow", "rad2deg", "repeat", - "root", "round", "roundn", "sec", "sgn", "shl", "shr", "sin", "sinh", "sqrt", "sum", - "switch", "tan", "tanh", "true", "trunc", "until", "while", "xnor", "xor", "&", "|" + "root", "round", "roundn", "sec", "sgn", "shl", "shr", "sin", "sinc", "sinh", "sqrt", + "sum", "switch", "tan", "tanh", "true", "trunc", "until", "while", "xnor", "xor", + "&", "|" }; static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string); @@ -851,6 +852,21 @@ namespace exprtk return erfc_impl(static_cast(v),real_type_tag()); } + template + inline T sinc_impl(T v, real_type_tag) + { + if(std::abs(v) >= std::numeric_limits::epsilon()) + return(std::sin(v) / v); + else + return T(1); + } + + template + inline T sinc_impl(T v, int_type_tag) + { + return erfc_impl(static_cast(v),real_type_tag()); + } + template inline T acos_impl(const T v, real_type_tag) { return std::acos (v); } template inline T acosh_impl(const T v, real_type_tag) { return std::log(v + std::sqrt((v * v) - T(1))); } template inline T asin_impl(const T v, real_type_tag) { return std::asin (v); } @@ -1123,6 +1139,7 @@ namespace exprtk exprtk_define_unary_function(pos ) exprtk_define_unary_function(round) exprtk_define_unary_function(sin ) + exprtk_define_unary_function(sinc ) exprtk_define_unary_function(sinh ) exprtk_define_unary_function(sqrt ) exprtk_define_unary_function(tan ) @@ -3129,14 +3146,15 @@ namespace exprtk e_exp , e_expm1 , e_floor , e_log , e_log10 , e_log2 , e_log1p , e_logn , e_neg , e_pos , e_round , e_roundn , - e_root , e_sqrt , e_sin , e_sinh , - e_sec , e_csc , e_tan , e_tanh , - e_cot , e_clamp , e_iclamp , e_inrange , - e_sgn , e_r2d , e_d2r , e_d2g , - e_g2d , e_hypot , e_notl , e_erf , - e_erfc , e_frac , e_trunc , e_assign , - e_addass , e_subass , e_mulass , e_divass , - e_in , e_like , e_ilike , e_multi , + e_root , e_sqrt , e_sin , e_sinc , + e_sinh , e_sec , e_csc , e_tan , + e_tanh , e_cot , e_clamp , e_iclamp , + e_inrange , e_sgn , e_r2d , e_d2r , + e_d2g , e_g2d , e_hypot , e_notl , + e_erf , e_erfc , e_frac , e_trunc , + e_assign , e_addass , e_subass , e_mulass , + e_divass , e_in , e_like , e_ilike , + e_multi , // Do not add new functions/operators after this point. e_sf00 = 1000, e_sf01 = 1001, e_sf02 = 1002, e_sf03 = 1003, @@ -3220,6 +3238,7 @@ namespace exprtk case e_pos : return numeric::pos (arg); case e_round : return numeric::round(arg); case e_sin : return numeric::sin (arg); + case e_sinc : return numeric::sinc (arg); case e_sinh : return numeric::sinh (arg); case e_sqrt : return numeric::sqrt (arg); case e_tan : return numeric::tan (arg); @@ -3376,18 +3395,18 @@ namespace exprtk e_cos , e_cosh , e_exp , e_expm1 , e_floor , e_log , e_log10 , e_log2 , e_log1p , e_neg , e_pos , e_round , - e_sin , e_sinh , e_sqrt , e_tan , - e_tanh , e_cot , e_sec , e_csc , - e_r2d , e_d2r , e_d2g , e_g2d , - e_notl , e_sgn , e_erf , e_erfc , - e_frac , e_trunc , e_uvouv , e_vov , - e_cov , e_voc , e_vob , e_bov , - e_cob , e_boc , e_vovov , e_vovoc , - e_vocov , e_covov , e_covoc , e_vovovov , - e_vovovoc , e_vovocov , e_vocovov , e_covovov , - e_covocov , e_vocovoc , e_covovoc , e_vococov , - e_sf3ext , e_sf4ext , e_nulleq , e_vecelem , - e_break , e_continue + e_sin , e_sinc , e_sinh , e_sqrt , + e_tan , e_tanh , e_cot , e_sec , + e_csc , e_r2d , e_d2r , e_d2g , + e_g2d , e_notl , e_sgn , e_erf , + e_erfc , e_frac , e_trunc , e_uvouv , + e_vov , e_cov , e_voc , e_vob , + e_bov , e_cob , e_boc , e_vovov , + e_vovoc , e_vocov , e_covov , e_covoc , + e_vovovov , e_vovovoc , e_vovocov , e_vocovov , + e_covovov , e_covocov , e_vocovoc , e_covovoc , + e_vococov , e_sf3ext , e_sf4ext , e_nulleq , + e_vecelem , e_break , e_continue }; typedef T value_type; @@ -6199,6 +6218,7 @@ namespace exprtk exprtk_define_unary_op(sec ) exprtk_define_unary_op(sgn ) exprtk_define_unary_op(sin ) + exprtk_define_unary_op(sinc ) exprtk_define_unary_op(sinh ) exprtk_define_unary_op(sqrt ) exprtk_define_unary_op(tan ) @@ -7398,7 +7418,9 @@ namespace exprtk struct mode0 { - static inline T process(const T& t0, const T& t1, const T& t2, const T& t3, const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) { // (T0 o0 T1) o1 (T2 o2 T3) return bf1(bf0(t0,t1),bf2(t2,t3)); @@ -7417,7 +7439,9 @@ namespace exprtk struct mode1 { - static inline T process(const T& t0, const T& t1, const T& t2, const T& t3, const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) { // (T0 o0 (T1 o1 (T2 o2 T3)) return bf0(t0,bf1(t1,bf2(t2,t3))); @@ -7435,7 +7459,9 @@ namespace exprtk struct mode2 { - static inline T process(const T& t0, const T& t1, const T& t2, const T& t3, const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) { // (T0 o0 ((T1 o1 T2) o2 T3) return bf0(t0,bf2(bf1(t1,t2),t3)); @@ -7454,7 +7480,9 @@ namespace exprtk struct mode3 { - static inline T process(const T& t0, const T& t1, const T& t2, const T& t3, const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) { // (((T0 o0 T1) o1 T2) o2 T3) return bf2(bf1(bf0(t0,t1),t2),t3); @@ -7473,7 +7501,9 @@ namespace exprtk struct mode4 { - static inline T process(const T& t0, const T& t1, const T& t2, const T& t3, const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) + static inline T process(const T& t0, const T& t1, + const T& t2, const T& t3, + const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2) { // ((T0 o0 (T1 o1 T2)) o2 T3 return bf2(bf0(t0,bf1(t1,t2)),t3); @@ -9306,6 +9336,7 @@ namespace exprtk register_op( "log1p",e_log1p , 1) register_op( "round",e_round , 1) register_op( "sin",e_sin , 1) + register_op( "sinc",e_sinc , 1) register_op( "sinh",e_sinh , 1) register_op( "sec",e_sec , 1) register_op( "csc",e_csc , 1) @@ -9347,14 +9378,16 @@ namespace exprtk { public: - explicit ifunction(const std::size_t& pc) - : param_count(pc) + explicit ifunction(const std::size_t& pc, const bool hse = true) + : param_count(pc), + has_side_effects(hse) {} virtual ~ifunction() {} std::size_t param_count; + bool has_side_effects; inline virtual T operator()() { @@ -10855,7 +10888,7 @@ namespace exprtk } } - bool update_error(type& error, const std::string& expression) + inline bool update_error(type& error, const std::string& expression) { if ( expression.empty() || @@ -11930,7 +11963,10 @@ namespace exprtk { expression_node_ptr result = expression_generator_.function(function); next_token(); - if (token_is(token_t::e_lbracket) && (!token_is(token_t::e_rbracket))) + if ( + token_is(token_t::e_lbracket) && + !token_is(token_t::e_rbracket) + ) { set_error( make_error(parser_error::e_syntax, @@ -13373,7 +13409,7 @@ namespace exprtk } else if (token_is(token_t::e_rsqrbracket)) { - return expression_generator_(vec->size()); + return expression_generator_(T(vec->size())); } else if (0 == (index_expr = parse_expression())) { @@ -14161,15 +14197,16 @@ namespace exprtk (details::e_log10 == operation) || (details::e_log2 == operation) || (details::e_log1p == operation) || (details::e_neg == operation) || (details::e_pos == operation) || (details::e_round == operation) || - (details::e_sin == operation) || (details::e_sinh == operation) || - (details::e_sqrt == operation) || (details::e_tan == operation) || - (details::e_tanh == operation) || (details::e_cot == operation) || - (details::e_sec == operation) || (details::e_csc == operation) || - (details::e_r2d == operation) || (details::e_d2r == operation) || - (details::e_d2g == operation) || (details::e_g2d == operation) || - (details::e_notl == operation) || (details::e_sgn == operation) || - (details::e_erf == operation) || (details::e_erfc == operation) || - (details::e_frac == operation) || (details::e_trunc == operation); + (details::e_sin == operation) || (details::e_sinc == operation) || + (details::e_sinh == operation) || (details::e_sqrt == operation) || + (details::e_tan == operation) || (details::e_tanh == operation) || + (details::e_cot == operation) || (details::e_sec == operation) || + (details::e_csc == operation) || (details::e_r2d == operation) || + (details::e_d2r == operation) || (details::e_d2g == operation) || + (details::e_g2d == operation) || (details::e_notl == operation) || + (details::e_sgn == operation) || (details::e_erf == operation) || + (details::e_erfc == operation) || (details::e_frac == operation) || + (details::e_trunc == operation); } inline bool sf3_optimizable(const std::string& sf3id, trinary_functor_t& tfunc) @@ -14881,6 +14918,7 @@ namespace exprtk case_stmt(details:: e_pos,details:: pos_op) \ case_stmt(details::e_round,details::round_op) \ case_stmt(details:: e_sin,details:: sin_op) \ + case_stmt(details:: e_sinc,details:: sinc_op) \ case_stmt(details:: e_sinh,details:: sinh_op) \ case_stmt(details:: e_sqrt,details:: sqrt_op) \ case_stmt(details:: e_tan,details:: tan_op) \ @@ -19822,7 +19860,7 @@ namespace exprtk // Attempt simple constant folding optimization. expression_node_ptr expression_point = node_allocator_->allocate(f); dynamic_cast(expression_point)->init_branches(branch); - if (is_constant_foldable(branch)) + if (is_constant_foldable(branch) && !f->has_side_effects) { Type v = expression_point->value(); details::free_node(*node_allocator_,expression_point); @@ -19924,6 +19962,7 @@ namespace exprtk register_unary_op(details:: e_pos,details:: pos_op) register_unary_op(details::e_round,details::round_op) register_unary_op(details:: e_sin,details:: sin_op) + register_unary_op(details:: e_sinc,details:: sinc_op) register_unary_op(details:: e_sinh,details:: sinh_op) register_unary_op(details:: e_sqrt,details:: sqrt_op) register_unary_op(details:: e_tan,details:: tan_op) @@ -20496,7 +20535,9 @@ namespace exprtk public: - polynomial() : exprtk::ifunction((N+2 <= 20) ? (N + 2) : std::numeric_limits::max()) {} + polynomial() + : exprtk::ifunction((N+2 <= 20) ? (N + 2) : std::numeric_limits::max(),false) + {} inline virtual T operator()(const T& x, const T& c1, const T& c0) { diff --git a/readme.txt b/readme.txt index 670107b..db8c32e 100644 --- a/readme.txt +++ b/readme.txt @@ -13,36 +13,37 @@ easily extendible. The ExprTk expression evaluator supports the following fundamental arithmetic operations, functions and processes: - (0) Basic operators: +, -, *, /, %, ^ + (00) Basic operators: +, -, *, /, %, ^ - (1) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, - expm1, floor, frac, log, log10, log1p, log2, - logn, max, min, mul, nequal, root, round, - roundn, sgn, sqrt, sum, trunc + (01) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, + expm1, floor, frac, log, log10, log1p, log2, + logn, max, min, mul, nequal, root, round, + roundn, sgn, sqrt, sum, trunc - (2) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, - cos, cosh, cot, csc, sec, sin, sinh, tan, tanh, - hypot, rad2deg, deg2grad, deg2rad, grad2deg + (02) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, + cos, cosh, cot, csc, sec, sin, sinc, sinh, tan, + tanh, hypot, rad2deg, deg2grad, deg2rad, + grad2deg - (3) Equalities & - Inequalities: =, ==, <>, !=, <, <=, >, >= + (03) Equalities & + Inequalities: =, ==, <>, !=, <, <=, >, >= - (4) Boolean logic: and, mand, mor, nand, nor, not, or, shl, shr, - xnor, xor, true, false + (04) Boolean logic: and, mand, mor, nand, nor, not, or, shl, shr, + xnor, xor, true, false - (5) Conditional, - Switch & - Loop statements: if-then-else, ternary conditional, switch-case, - while, for, repeat-until, break, continue + (05) Control + structures: if-then-else, ternary conditional, switch-case - (6) Assignment: :=, +=, -=, *=, /= + (06) Loop statements: while, for, repeat-until, break, continue - (7) String - processing: in, like, ilike + (07) Assignment: :=, +=, -=, *=, /= - (8) Optimisations: constant-folding and simple strength reduction + (08) String + processing: in, like, ilike - (9) Calculus: numerical integration and differentiation + (09) Optimisations: constant-folding and simple strength reduction + + (10) Calculus: numerical integration and differentiation @@ -331,6 +332,8 @@ include path (e.g: /usr/include/). +----------+---------------------------------------------------------+ | sin | Sine of x. (eg: sin(x)) | +----------+---------------------------------------------------------+ +| sinc | Sine cardinal of x. (eg: sinc(x)) | ++----------+---------------------------------------------------------+ | sinh | Hyperbolic sine of x. (eg: sinh(x)) | +----------+---------------------------------------------------------+ | tan | Tangent of x. (eg: tan(x)) | @@ -891,25 +894,32 @@ correctly optimize such expressions for a given architecture. (19) Recursive calls made from within composited functions will have a stack size bound by the stack of the executing architecture. - (20) The entity relationship between symbol_table and an expression + (20) User defined functions by default are assumed to have side + effects. As such an "all constant parameter" invocation of such + functions wont result in constant folding. If the function has + no side effects then that can be noted during the constructor + of the ifunction allowing it to be constant folded where + appropriate. + + (21) The entity relationship between symbol_table and an expression is one-to-many. Hence the intended use case is to have a single symbol table manage the variable and function requirements of multiple expressions. - (21) The common use-case for an expression is to have it compiled + (22) The common use-case for an expression is to have it compiled only ONCE and then subsequently have it evaluated multiple times. An extremely inefficient and suboptimal approach would be to recompile an expression from its string form every time it requires evaluating. - (22) The following are examples of compliant floating point value + (23) The following are examples of compliant floating point value representations: (a) 12345 (b) -123.456 (c) +123.456e+12 (d) 123.456E-12 (e) +012.045e+07 (f) .1234 (g) 123.456f (h) -321.654E+3L - (23) Expressions may contain any of the following comment styles: + (24) Expressions may contain any of the following comment styles: 1. // .... \n 2. # .... \n 3. /* .... */