From 7103525a7f766962aef1556a35bf3e74140f995f Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Wed, 27 Mar 2013 01:20:20 +1100 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 191 ++++++++++++++++++++++++++++++++++++++++++------ exprtk_test.cpp | 115 +++++++++++++++++++++-------- readme.txt | 13 ++-- 3 files changed, 259 insertions(+), 60 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 142b5bd..cea32f1 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -1504,7 +1504,7 @@ namespace exprtk s_end_ = str.data() + str.size(); eof_token_.set_operator(token_t::e_eof,s_end_,s_end_,base_itr_); token_list_.clear(); - while (s_end_ != s_itr_) + while (!is_end(s_itr_)) { scan_token(); if (token_list_.back().is_error()) @@ -1564,9 +1564,14 @@ namespace exprtk private: + inline bool is_end(const char* itr) + { + return (s_end_ == itr); + } + inline void skip_whitespace() { - while ((s_end_ != s_itr_) && details::is_whitespace(*s_itr_)) + while (!is_end(s_itr_) && details::is_whitespace(*s_itr_)) { ++s_itr_; } @@ -1575,15 +1580,48 @@ namespace exprtk inline void skip_comments() { #ifndef exprtk_disable_comments - if ((s_end_ == s_itr_) || (s_end_ == (s_itr_ + 1))) + //The following comment styles are supported: + // 1. // .... \n + // 2. # .... \n + // 3. /* .... */ + struct test + { + static inline bool comment_start(const char c0, const char c1, int& mode, int& incr) + { + mode = 0; + if ('#' == c0) { mode = 1; incr = 1; } + else if ('/' == c0) + { + if ('/' == c1) { mode = 1; incr = 2; } + else if ('*' == c1) { mode = 2; incr = 2; } + } + return (mode != 0); + } + + static inline bool comment_end(const char c0, const char c1, const int mode) + { + return ((1 == mode) && ('\n' == c0)) || + ((2 == mode) && ( '*' == c0) && ('/' == c1)); + } + }; + + int mode = 0; + int increment = 0; + if (is_end(s_itr_) || is_end((s_itr_ + 1))) return; - else if (('/' != *s_itr_) || ('/' != *(s_itr_ + 1))) + else if (!test::comment_start(*s_itr_,*(s_itr_ + 1),mode,increment)) return; - while ((s_end_ != s_itr_) && ('\n' != *s_itr_)) + s_itr_ += increment; + while (!is_end(s_itr_) && !test::comment_end(*s_itr_,*(s_itr_ + 1),mode)) { ++s_itr_; } - skip_whitespace(); + if (is_end(s_itr_)) + { + s_itr_ += mode; + skip_whitespace(); + skip_comments(); + } #endif } @@ -1591,7 +1629,7 @@ namespace exprtk { skip_whitespace(); skip_comments(); - if (s_end_ == s_itr_) + if (is_end(s_itr_)) { return; } @@ -1635,7 +1673,7 @@ namespace exprtk { token_t t; - if ((s_itr_ + 1) != s_end_) + if (!is_end(s_itr_ + 1)) { token_t::token_type ttype = token_t::e_none; char c0 = s_itr_[0]; @@ -1675,7 +1713,7 @@ namespace exprtk { const char* begin = s_itr_; while ( - (s_end_ != s_itr_) && + (!is_end(s_itr_)) && (details::is_letter_or_digit(*s_itr_) || ((*s_itr_) == '_')) ) { @@ -1705,7 +1743,7 @@ namespace exprtk bool post_e_sign_found = false; token_t t; - while (s_end_ != s_itr_) + while (!is_end(s_itr_)) { if ('.' == (*s_itr_)) { @@ -1723,7 +1761,7 @@ namespace exprtk { const char& c = *(s_itr_ + 1); - if (s_end_ == (s_itr_ + 1)) + if (is_end(s_itr_ + 1)) { t.set_error(token::e_err_number,begin,s_itr_,base_itr_); token_list_.push_back(t); @@ -1778,7 +1816,6 @@ namespace exprtk { t.set_error(token::e_err_sfunc,begin,s_itr_,base_itr_); token_list_.push_back(t); - return; } @@ -1791,7 +1828,6 @@ namespace exprtk { t.set_error(token::e_err_sfunc,begin,s_itr_,base_itr_); token_list_.push_back(t); - return; } @@ -1812,14 +1848,13 @@ namespace exprtk { t.set_error(token::e_err_string,begin,s_itr_,base_itr_); token_list_.push_back(t); - return; } ++s_itr_; bool escaped = false; - while (s_end_ != s_itr_) + while (!is_end(s_itr_)) { if ('\\' == *s_itr_) { @@ -1834,16 +1869,16 @@ namespace exprtk } else if (escaped) escaped = false; - ++s_itr_; } - if (s_end_ == s_itr_) + if (is_end(s_itr_)) { t.set_error(token::e_err_string,begin,s_itr_,base_itr_); token_list_.push_back(t); return; } + t.set_string(begin,s_itr_,base_itr_); token_list_.push_back(t); ++s_itr_; @@ -10775,7 +10810,7 @@ namespace exprtk if (!expr_gen.sf3_optimizable(id,sf3opr)) return false; else - result = synthesize_sf3ext_expression::process(expr_gen,sf3opr,t0,t1,t2); + result = synthesize_sf3ext_expression::template process(expr_gen,sf3opr,t0,t1,t2); return true; } @@ -10846,7 +10881,7 @@ namespace exprtk if (!expr_gen.sf4_optimizable(id,sf4opr)) return false; else - result = synthesize_sf4ext_expression::process(expr_gen,sf4opr,t0,t1,t2,t3); + result = synthesize_sf4ext_expression::template process(expr_gen,sf4opr,t0,t1,t2,t3); return true; } @@ -13874,7 +13909,7 @@ namespace exprtk template inline T derivative(expression& e, T& x, - const T& h = T(0.00001)) + const T& h = T(0.00000001)) { T x_init = x; x = x_init + T(2.0) * h; @@ -13889,10 +13924,47 @@ namespace exprtk return (-y0 + T(8.0) * (y1 - y2) + y3) / (T(12.0) * h); } + template + inline T second_derivative(expression& e, + T& x, + const T& h = T(0.00001)) + { + T y = e.value(); + T x_init = x; + x = x_init + T(2.0) * h; + T y0 = e.value(); + x = x_init + h; + T y1 = e.value(); + x = x_init - h; + T y2 = e.value(); + x = x_init - T(2.0) * h; + T y3 = e.value(); + x = x_init; + return (-y0 + T(16.0) * (y1 + y2) - T(30.0) * y - y3) / (T(12.0) * h * h); + } + + template + inline T third_derivative(expression& e, + T& x, + const T& h = T(0.0001)) + { + T x_init = x; + x = x_init + T(2.0) * h; + T y0 = e.value(); + x = x_init + h; + T y1 = e.value(); + x = x_init - h; + T y2 = e.value(); + x = x_init - T(2.0) * h; + T y3 = e.value(); + x = x_init; + return (y0 + T(2.0) * (y2 - y1) - y3) / (T(2.0) * h * h * h); + } + template inline T derivative(expression& e, const std::string& variable_name, - const T& h = T(0.00001)) + const T& h = T(0.00000001)) { symbol_table& sym_table = e.get_symbol_table(); if (!sym_table.valid()) @@ -13904,7 +13976,48 @@ namespace exprtk T x_original = x; T result = derivative(e,x,h); x = x_original; + return result; + } + else + return std::numeric_limits::quiet_NaN(); + } + template + inline T second_derivative(expression& e, + const std::string& variable_name, + const T& h = T(0.00001)) + { + symbol_table& sym_table = e.get_symbol_table(); + if (!sym_table.valid()) + return std::numeric_limits::quiet_NaN(); + details::variable_node* var = sym_table.get_variable(variable_name); + if (var) + { + T& x = var->ref(); + T x_original = x; + T result = second_derivative(e,x,h); + x = x_original; + return result; + } + else + return std::numeric_limits::quiet_NaN(); + } + + template + inline T third_derivative(expression& e, + const std::string& variable_name, + const T& h = T(0.0001)) + { + symbol_table& sym_table = e.get_symbol_table(); + if (!sym_table.valid()) + return std::numeric_limits::quiet_NaN(); + details::variable_node* var = sym_table.get_variable(variable_name); + if (var) + { + T& x = var->ref(); + T x_original = x; + T result = third_derivative(e,x,h); + x = x_original; return result; } else @@ -14016,6 +14129,32 @@ namespace exprtk template struct poly_impl { }; + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c12, const Type c11, const Type c10, const Type c9, const Type c8, + const Type c7, const Type c6, const Type c5, const Type c4, const Type c3, + const Type c2, const Type c1, const Type c0) + { + // p(x) = c_12x^12 + c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((((((((c12 * x + c11) * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + + template + struct poly_impl + { + static inline T evaluate(const Type x, + const Type c11, const Type c10, const Type c9, const Type c8, const Type c7, + const Type c6, const Type c5, const Type c4, const Type c3, const Type c2, + const Type c1, const Type c0) + { + // p(x) = c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0 + return ((((((((((( c11 * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0); + } + }; + template struct poly_impl { @@ -14183,6 +14322,16 @@ namespace exprtk return ((10 == N) ? poly_impl::evaluate(x,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0) : std::numeric_limits::quiet_NaN()); } + inline virtual T operator()(const T& x, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) + { + return ((11 == N) ? poly_impl::evaluate(x,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0) : std::numeric_limits::quiet_NaN()); + } + + inline virtual T operator()(const T& x, const T& c12, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) + { + return ((12 == N) ? poly_impl::evaluate(x,c12,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0) : std::numeric_limits::quiet_NaN()); + } + inline virtual T operator()() { return std::numeric_limits::quiet_NaN(); diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 632797d..c4ccc20 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -1496,25 +1496,69 @@ inline bool run_test07() return false; } - for (x = T(-200.0); x < T(200); x+=T(0.0001)) + for (x = T(-200.0); x < T(200); x += T(0.0001)) { - T result1 = exprtk::derivative(expression,x); - T result2 = exprtk::derivative(expression,"x"); - T real_result = T(2.0) * std::cos(T(2.0) * x + T(1.0/3.0)); - - if (not_equal(result1,result2,T(0.000000001))) { - printf("run_test07() - Derivative Error: result1 != result2\n"); - return false; + T deriv1_real_result = T(2.0) * std::cos(T(2.0) * x + T(1.0/3.0)); + T deriv1_result1 = exprtk::derivative(expression,x); + T deriv1_result2 = exprtk::derivative(expression,"x"); + + if (not_equal(deriv1_result1,deriv1_result2,T(0.00001))) + { + printf("run_test07() - 1st Derivative Error: result1 != result2\n"); + return false; + } + + if (not_equal(deriv1_result1,deriv1_real_result,T(0.00001))) + { + printf("run_test07() - 1st Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", + x, + deriv1_real_result, + deriv1_result1); + return false; + } } - if (not_equal(result1,real_result,T(0.000000001))) { - printf("run_test07() - Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", - x, - real_result, - result1); - return false; + T deriv2_real_result = T(-4.0) * std::sin(T(2.0) * x + T(1.0/3.0)); + T deriv2_result1 = exprtk::second_derivative(expression,x); + T deriv2_result2 = exprtk::second_derivative(expression,"x"); + + if (not_equal(deriv2_result1,deriv2_result2,T(0.0000001))) + { + printf("run_test07() - 2nd Derivative Error: result1 != result2\n"); + return false; + } + + if (not_equal(deriv2_result1,deriv2_real_result,T(0.01))) + { + printf("run_test07() - 2nd Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", + x, + deriv2_real_result, + deriv2_result1); + return false; + } + } + + { + T deriv3_real_result = T(-8.0) * std::cos(T(2.0) * x + T(1.0/3.0)); + T deriv3_result1 = exprtk::third_derivative(expression,x); + T deriv3_result2 = exprtk::third_derivative(expression,"x"); + + if (not_equal(deriv3_result1,deriv3_result2,T(0.0000001))) + { + printf("run_test07() - 3rd Derivative Error: result1 != result2\n"); + return false; + } + + if (not_equal(deriv3_result1,deriv3_real_result,T(0.01))) + { + printf("run_test07() - 3rd Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", + x, + deriv3_real_result, + deriv3_result1); + return false; + } } } @@ -2332,15 +2376,18 @@ inline bool run_test12() typedef exprtk::expression expression_t; static const std::string expression_string[] = { - "equal(poly1(x,2.2,1.1),(2.2x^1+1.1))", - "equal(poly2(x,3.3,2.2,1.1),(3.3x^2+2.2x^1+1.1))", - "equal(poly3(x,4.4,3.3,2.2,1.1),(4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly4(x,5.5,4.4,3.3,2.2,1.1),(5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly5(x,6.6,5.5,4.4,3.3,2.2,1.1),(6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly6(x,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly7(x,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly8(x,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", - "equal(poly9(x,1.1,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(1.1x^9+9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))" + "equal(poly01(x,2.2,1.1),(2.2x^1+1.1))", + "equal(poly02(x,3.3,2.2,1.1),(3.3x^2+2.2x^1+1.1))", + "equal(poly03(x,4.4,3.3,2.2,1.1),(4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly04(x,5.5,4.4,3.3,2.2,1.1),(5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly05(x,6.6,5.5,4.4,3.3,2.2,1.1),(6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly06(x,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly07(x,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly08(x,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))", + "equal(poly09(x,1.1,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(1.1x^9+9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))" + "equal(poly10(x,2.2,1.1,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(2.2x^10+1.1x^9+9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))" + "equal(poly11(x,3.3,2.2,1.1,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(3.3x^11+2.2x^10+1.1x^9+9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))" + "equal(poly12(x,4.4,3.3,2.2,1.1,9.9,8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1),(4.4x^12+3.3x^11+2.2x^10+1.1x^9+9.9x^8+8.8x^7+7.7x^6+6.6x^5+5.5x^4+4.4x^3+3.3x^2+2.2x^1+1.1))" }; static const std::size_t expression_string_size = sizeof(expression_string) / sizeof(std::string); @@ -2356,20 +2403,24 @@ inline bool run_test12() exprtk::polynomial poly08; exprtk::polynomial poly09; exprtk::polynomial poly10; + exprtk::polynomial poly11; + exprtk::polynomial poly12; exprtk::symbol_table symbol_table; symbol_table.add_variable("x",x); - symbol_table.add_function( "poly1", poly01); - symbol_table.add_function( "poly2", poly02); - symbol_table.add_function( "poly3", poly03); - symbol_table.add_function( "poly4", poly04); - symbol_table.add_function( "poly5", poly05); - symbol_table.add_function( "poly6", poly06); - symbol_table.add_function( "poly7", poly07); - symbol_table.add_function( "poly8", poly08); - symbol_table.add_function( "poly9", poly09); + symbol_table.add_function("poly01", poly01); + symbol_table.add_function("poly02", poly02); + symbol_table.add_function("poly03", poly03); + symbol_table.add_function("poly04", poly04); + symbol_table.add_function("poly05", poly05); + symbol_table.add_function("poly06", poly06); + symbol_table.add_function("poly07", poly07); + symbol_table.add_function("poly08", poly08); + symbol_table.add_function("poly09", poly09); symbol_table.add_function("poly10", poly10); + symbol_table.add_function("poly11", poly11); + symbol_table.add_function("poly12", poly12); expression_t expression; expression.register_symbol_table(symbol_table); diff --git a/readme.txt b/readme.txt index 992edda..597b0a6 100644 --- a/readme.txt +++ b/readme.txt @@ -136,7 +136,7 @@ Expression Library can be found at: +-----------+--------------------------------------------------------+ | OPERATOR | DEFINITION | +-----------+--------------------------------------------------------+ -| == or = | True only if x is strictly equal to y. (eg: x == y) | +| == or = | True only if x is strictly equal to y. (eg: x == y) | +-----------+--------------------------------------------------------+ | <> or != | True only if x does not equal y (eg: x <> y or x != y) | +-----------+--------------------------------------------------------+ @@ -406,7 +406,7 @@ correctly optimize such expressions for a given architecture. (10) User defined functions can have up to 20 parameters. - (11) The inbuilt polynomial functions can be at most of degree 10. + (11) The inbuilt polynomial functions can be at most of degree 12. (12) Where appropriate constant folding optimisations will be applied. (eg: The expression '2+(3-(x/y))' becomes '5-(x/y)') @@ -415,11 +415,10 @@ correctly optimize such expressions for a given architecture. To turn them off, the following needs to be defined at compile time: exprtk_disable_string_capabilities - (14) Expressions may contain trailing comments that must be prefixed - with '//' and are terminated by the next occurrence of new-line - or line-break. To disallow comments, the following needs to be - defined at compile time: exprtk_disable_comments - (eg: '2+(3-(x/y)) // This is an expression') + (14) Expressions may contain any of the following comment styles: + 1. // .... \n + 2. # .... \n + 3. /* .... */