From f0f19b9851f3d7cfca6f794ab43367263cbd4c9e Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Sun, 7 Apr 2013 17:56:41 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 593 +++++++++++++++++++++++++++++++++++++++++++++++- exprtk_test.cpp | 158 +++++++++++++ 2 files changed, 743 insertions(+), 8 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 0c67564..ddcf1a7 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -2394,6 +2394,15 @@ namespace exprtk public: + bool remove(const std::string& target_symbol) + { + replace_map_t::iterator itr = replace_map_.find(target_symbol); + if (replace_map_.end() == itr) + return false; + replace_map_.erase(itr); + return true; + } + bool add_replace(const std::string& target_symbol, const std::string& replace_symbol, const lexer::token::token_type token_type = lexer::token::e_symbol) @@ -9337,17 +9346,17 @@ namespace exprtk std::string diagnostic = "ERR03 - "; switch (lexer_[i].type) { - case lexer::token::e_error : diagnostic + "General token error"; + case lexer::token::e_error : diagnostic += "General token error"; break; - case lexer::token::e_err_symbol : diagnostic + "Symbol error"; + case lexer::token::e_err_symbol : diagnostic += "Symbol error"; break; - case lexer::token::e_err_number : diagnostic + "Invalid numeric token"; + case lexer::token::e_err_number : diagnostic += "Invalid numeric token"; break; - case lexer::token::e_err_string : diagnostic + "Invalid string token"; + case lexer::token::e_err_string : diagnostic += "Invalid string token"; break; - case lexer::token::e_err_sfunc : diagnostic + "Invalid special function token"; + case lexer::token::e_err_sfunc : diagnostic += "Invalid special function token"; break; - default : diagnostic + "Unknown compiler error"; + default : diagnostic += "Unknown compiler error"; break; } set_error( @@ -9502,14 +9511,24 @@ namespace exprtk inline bool replace_symbol(const std::string& old_symbol, const std::string& new_symbol) { - if (details::is_reserved_word(old_symbol)) + if (!replacer_enabled()) return false; - else if (!replacer_enabled()) + else if (details::is_reserved_word(old_symbol)) return false; else return symbol_replacer_.add_replace(old_symbol,new_symbol,lexer::token::e_symbol); } + inline bool remove_replace_symbol(const std::string& symbol) + { + if (!replacer_enabled()) + return false; + else if (details::is_reserved_word(symbol)) + return false; + else + return symbol_replacer_.remove(symbol); + } + private: inline bool valid_base_operation(const std::string& symbol) @@ -15558,6 +15577,564 @@ namespace exprtk }; + template + class function_compositor + { + public: + + typedef exprtk::expression expression_t; + typedef exprtk::symbol_table symbol_table_t; + typedef exprtk::parser parser_t; + + struct function + { + function(const std::string& n) + : name_(n) + {} + + inline function& name(const std::string& n) + { + name_ = n; + return (*this); + } + + inline function& expression(const std::string& e) + { + expression_ = e; + return (*this); + } + + inline function& var(const std::string& v) + { + v_.push_back(v); + return (*this); + } + + std::string name_; + std::string expression_; + std::deque v_; + }; + + private: + + struct base_func : public exprtk::ifunction + { + typedef const T& type; + + base_func() + : exprtk::ifunction(0) + { + clear(); + } + + base_func(T& v0) + : exprtk::ifunction(1) + { + clear(); + v[0] = &v0; + } + + base_func(T& v0, T& v1) + : exprtk::ifunction(2) + { + clear(); + v[0] = &v0; v[1] = &v1; + } + + base_func(T& v0, T& v1, T& v2) + : exprtk::ifunction(3) + { + clear(); + v[0] = &v0; v[1] = &v1; + v[2] = &v2; + } + + base_func(T& v0, T& v1, T& v2, T& v3) + : exprtk::ifunction(4) + { + clear(); + v[0] = &v0; v[1] = &v1; + v[2] = &v2; v[3] = &v3; + } + + base_func(T& v0, T& v1, T& v2, T& v3, T& v4) + : exprtk::ifunction(5) + { + clear(); + v[0] = &v0; v[1] = &v1; + v[2] = &v2; v[3] = &v3; + v[4] = &v4; + } + + base_func(T& v0, T& v1, T& v2, T& v3, T& v4, T& v5) + : exprtk::ifunction(6) + { + clear(); + v[0] = &v0; v[1] = &v1; + v[2] = &v2; v[3] = &v3; + v[4] = &v4; v[5] = &v5; + } + + inline void update(const T& v0) + { + (*v[0]) = v0; + } + + inline void update(const T& v0, const T& v1) + { + (*v[0]) = v0; (*v[1]) = v1; + } + + inline void update(const T& v0, const T& v1, const T& v2) + { + (*v[0]) = v0; (*v[1]) = v1; + (*v[2]) = v2; + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3) + { + (*v[0]) = v0; (*v[1]) = v1; + (*v[2]) = v2; (*v[3]) = v3; + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) + { + (*v[0]) = v0; (*v[1]) = v1; + (*v[2]) = v2; (*v[3]) = v3; + (*v[4]) = v4; + } + + inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) + { + (*v[0]) = v0; (*v[1]) = v1; + (*v[2]) = v2; (*v[3]) = v3; + (*v[4]) = v4; (*v[5]) = v5; + } + + enum { max_parameters = 6 }; + + inline void clear() + { + std::fill_n(v,(int)max_parameters,reinterpret_cast(0)); + } + + T* v[max_parameters]; + }; + + struct func_0param : public base_func + { + func_0param() : base_func() {} + + func_0param(expression_t& expr) + : base_func(), + expression(expr) + {} + + inline T operator()() + { + return expression.value(); + } + + expression_t expression; + }; + + typedef const T& type; + + struct func_1param : public base_func + { + func_1param() : base_func() {} + + func_1param(expression_t& expr, T& v0) + : base_func(v0), + expression(expr) + {} + + inline T operator()(type v0) + { + base_func::update(v0); + return expression.value(); + } + + expression_t expression; + }; + + struct func_2param : public base_func + { + func_2param() : base_func() {} + + func_2param(expression_t& expr, T& v0, T& v1) + : base_func(v0,v1), + expression(expr) + {} + + inline T operator()(type v0, type v1) + { + base_func::update(v0,v1); + return expression.value(); + } + + expression_t expression; + }; + + struct func_3param : public base_func + { + func_3param() : base_func() {} + + func_3param(expression_t& expr, T& v0, T& v1, T& v2) + : base_func(v0,v1,v2), + expression(expr) + {} + + inline T operator()(type v0, type v1, type v2) + { + base_func::update(v0,v1,v2); + return expression.value(); + } + + expression_t expression; + }; + + struct func_4param : public base_func + { + func_4param() : base_func() {} + + func_4param(expression_t& expr, T& v0, T& v1, T& v2, T& v3) + : base_func(v0,v1,v2,v3), + expression(expr) + {} + + inline T operator()(type v0, type v1, type v2, type v3) + { + base_func::update(v0,v1,v2,v3); + return expression.value(); + } + + expression_t expression; + }; + + struct func_5param : public base_func + { + func_5param() : base_func() {} + + func_5param(expression_t& expr, T& v0, T& v1, T& v2, T& v3, T& v4) + : base_func(v0,v1,v2,v3,v4), + expression(expr) + {} + + inline T operator()(type v0, type v1, type v2, type v3, type v4) + { + base_func::update(v0,v1,v2,v3,v4); + return expression.value(); + } + + expression_t expression; + }; + + struct func_6param : public base_func + { + func_6param() : base_func() {} + + func_6param(expression_t& expr, T& v0, T& v1, T& v2, T& v3, T& v4, T& v5) + : base_func(v0,v1,v2,v3,v4,v5), + expression(expr) + {} + + inline T operator()(type v0, type v1, type v2, type v3, type v4, type v5) + { + base_func::update(v0,v1,v2,v3,v4,v5); + return expression.value(); + } + + expression_t expression; + }; + + public: + + function_compositor() + : suffix_index_(1), + id_(get_id()) + {} + + function_compositor(const symbol_table_t& st) + : symbol_table_(st), + suffix_index_(1), + id_(get_id()) + {} + + inline symbol_table_t& symbol_table() + { + return symbol_table_; + } + + void clear() + { + symbol_table_.clear(); + expr_map_.clear(); + f0p_map_.clear(); + f1p_map_.clear(); + f2p_map_.clear(); + f3p_map_.clear(); + f4p_map_.clear(); + f5p_map_.clear(); + f6p_map_.clear(); + suffix_index_ = 1; + } + + inline bool add(const function& f) + { + switch(f.v_.size()) + { + case 0 : return add(f.name_,f.expression_); + case 1 : return add(f.name_,f.expression_,f.v_[0]); + case 2 : return add(f.name_,f.expression_,f.v_[0],f.v_[1]); + case 3 : return add(f.name_,f.expression_,f.v_[0],f.v_[1],f.v_[2]); + case 4 : return add(f.name_,f.expression_,f.v_[0],f.v_[1],f.v_[2],f.v_[3]); + case 5 : return add(f.name_,f.expression_,f.v_[0],f.v_[1],f.v_[2],f.v_[3],f.v_[4]); + case 6 : return add(f.name_,f.expression_,f.v_[0],f.v_[1],f.v_[2],f.v_[3],f.v_[4],f.v_[5]); + default : return false; + } + } + + inline bool add(const std::string& name, + const std::string& expression) + { + if (expr_map_.end() != expr_map_.find(name)) return false; + std::vector > var_transform_list; + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f0p_map_[name] = func_0param(expr_map_[name]); + return symbol_table_.add_function(name,f0p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0) + { + const std::size_t n = 1; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f1p_map_[name] = func_1param(expr_map_[name],(*v[0])); + return symbol_table_.add_function(name,f1p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1) + { + const std::size_t n = 2; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + else if (!add_variable(v1,v[1],sv[1])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + var_transform_list.push_back(std::make_pair(v1,sv[1])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f2p_map_[name] = func_2param(expr_map_[name],(*v[0]),(*v[1])); + return symbol_table_.add_function(name,f2p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, const std::string& v2) + { + const std::size_t n = 3; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + else if (!add_variable(v1,v[1],sv[1])) return false; + else if (!add_variable(v2,v[2],sv[2])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + var_transform_list.push_back(std::make_pair(v1,sv[1])); + var_transform_list.push_back(std::make_pair(v2,sv[2])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f3p_map_[name] = func_3param(expr_map_[name],(*v[0]),(*v[1]),(*v[2])); + return symbol_table_.add_function(name,f3p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, const std::string& v2, + const std::string& v3) + { + const std::size_t n = 4; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + else if (!add_variable(v1,v[1],sv[1])) return false; + else if (!add_variable(v2,v[2],sv[2])) return false; + else if (!add_variable(v3,v[3],sv[3])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + var_transform_list.push_back(std::make_pair(v1,sv[1])); + var_transform_list.push_back(std::make_pair(v2,sv[2])); + var_transform_list.push_back(std::make_pair(v3,sv[3])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f4p_map_[name] = func_4param(expr_map_[name],(*v[0]),(*v[1]),(*v[2]),(*v[3])); + return symbol_table_.add_function(name,f4p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, const std::string& v2, + const std::string& v3, const std::string& v4) + { + const std::size_t n = 5; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + else if (!add_variable(v1,v[1],sv[1])) return false; + else if (!add_variable(v2,v[2],sv[2])) return false; + else if (!add_variable(v3,v[3],sv[3])) return false; + else if (!add_variable(v3,v[4],sv[4])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + var_transform_list.push_back(std::make_pair(v1,sv[1])); + var_transform_list.push_back(std::make_pair(v2,sv[2])); + var_transform_list.push_back(std::make_pair(v3,sv[3])); + var_transform_list.push_back(std::make_pair(v4,sv[4])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f5p_map_[name] = func_5param(expr_map_[name],(*v[0]),(*v[1]),(*v[2]),(*v[3]),(*v[4])); + return symbol_table_.add_function(name,f5p_map_[name]); + } + + inline bool add(const std::string& name, + const std::string& expression, + const std::string& v0, const std::string& v1, const std::string& v2, + const std::string& v3, const std::string& v4, const std::string& v5) + { + const std::size_t n = 6; + T* v[n] = { 0 }; + std::string sv[n]; + if (expr_map_.end() != expr_map_.find(name)) return false; + else if (!add_variable(v0,v[0],sv[0])) return false; + else if (!add_variable(v1,v[1],sv[1])) return false; + else if (!add_variable(v2,v[2],sv[2])) return false; + else if (!add_variable(v3,v[3],sv[3])) return false; + else if (!add_variable(v3,v[4],sv[4])) return false; + else if (!add_variable(v3,v[5],sv[5])) return false; + std::vector > var_transform_list; + var_transform_list.push_back(std::make_pair(v0,sv[0])); + var_transform_list.push_back(std::make_pair(v1,sv[1])); + var_transform_list.push_back(std::make_pair(v2,sv[2])); + var_transform_list.push_back(std::make_pair(v3,sv[3])); + var_transform_list.push_back(std::make_pair(v4,sv[4])); + var_transform_list.push_back(std::make_pair(v5,sv[5])); + if (!compile_expression(name,expression,var_transform_list)) + return false; + else + f6p_map_[name] = func_6param(expr_map_[name],(*v[0]),(*v[1]),(*v[2]),(*v[3]),(*v[4]),(*v[5])); + return symbol_table_.add_function(name,f6p_map_[name]); + } + + private: + + template class Sequence> + bool compile_expression(const std::string& name, + const std::string& expression, + const Sequence,Allocator>& var_transform_list) + { + expression_t compiled_expression; + compiled_expression.register_symbol_table(symbol_table_); + + for (std::size_t i = 0; i < var_transform_list.size(); ++i) + { + parser_.remove_replace_symbol(var_transform_list[i].first); + if (!parser_.replace_symbol(var_transform_list[i].first,var_transform_list[i].second)) + return false; + } + + if (!parser_.compile(expression,compiled_expression)) + { + return false; + } + + for (std::size_t i = 0; i < var_transform_list.size(); ++i) + { + parser_.remove_replace_symbol(var_transform_list[i].first); + } + + expr_map_[name] = compiled_expression; + return true; + } + + bool add_variable(const std::string& v, T*& t, std::string& new_var) + { + static const unsigned int max_suffix_index = 1000000000; + while (suffix_index_ < max_suffix_index) + { + new_var = generate_name(v); + + bool used = symbol_table_.is_variable (new_var) || + symbol_table_.is_stringvar (new_var) || + symbol_table_.is_function (new_var) || + symbol_table_.is_vararg_function(new_var); + + if (!used) + { + symbol_table_.create_variable(new_var,T(0)); + t = 0; + t = &symbol_table_.get_variable(new_var)->ref(); + return (0 != t); + } + else + ++suffix_index_; + } + return false; + } + + std::string generate_name(const std::string v) + { + //eg: x --> function_compositor_1__x_123 + return std::string("function_compositor") + exprtk::details::to_str(id_) + "__" + + v + + exprtk::details::to_str(static_cast(suffix_index_)); + } + + unsigned int get_id() + { + static unsigned int base_id = 1; + return ++base_id; + } + + private: + + symbol_table_t symbol_table_; + parser_t parser_; + std::map expr_map_; + std::map f0p_map_; + std::map f1p_map_; + std::map f2p_map_; + std::map f3p_map_; + std::map f4p_map_; + std::map f5p_map_; + std::map f6p_map_; + unsigned int suffix_index_; + unsigned int id_; + }; + template inline bool pgo_primer() { diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 8c3e577..97438dd 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -3345,6 +3345,163 @@ inline bool run_test18() return !failure; } +template +inline bool run_test19() +{ + typedef exprtk::symbol_table symbol_table_t; + typedef exprtk::expression expression_t; + typedef exprtk::parser parser_t; + typedef exprtk::parser_error::type error_t; + typedef exprtk::function_compositor compositor_t; + typedef typename compositor_t::function function_t; + + { + T x = T(123.123); + + compositor_t fc; + + // f(x) = x + 2 + fc.add("f","x + 2","x"); + + // g(x) = x^2-3 + fc.add("g","x^2 - 3","x"); + + // fof(x) = f(f(x)) + fc.add("fof","f(f(x))","x"); + + // gog(x) = g(g(x)) + fc.add("gog","g(g(x))","x"); + + // fog(x) = f(g(x)) + fc.add("fog","f(g(x))","x"); + + // gof(x) = g(f(x)) + fc.add("gof","g(f(x))","x"); + + exprtk::symbol_table& symbol_table = fc.symbol_table(); + symbol_table.add_constants(); + symbol_table.add_variable("x",x); + + static const std::string expr_str_list[] = + { + "equal(f(x),(x + 2))", + "equal(g(x),(x^2 - 3))", + "equal(fof(x),(x + 4))", + "equal(gog(x),(x^4 - 6x^2 + 6))", + "equal(fog(x),(x^2 - 1))", + "equal(gof(x),(x^2 + 4x + 1))", + }; + static const std::size_t expr_str_list_size = sizeof(expr_str_list) / sizeof(std::string); + + std::deque expression_list; + + for (std::size_t i = 0; i < expr_str_list_size; ++i) + { + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expr_str_list[i],expression)) + { + printf("run_test19() - Error: %s Expression: %s\n", + parser.error().c_str(), + expr_str_list[i].c_str()); + return false; + } + else + expression_list.push_back(expression); + } + + bool failure = false; + + for (std::size_t i = 0; i < expression_list.size(); ++i) + { + if (T(1) != expression_list[i].value()) + { + printf("run_test19() - Error in evaluation! (1) Expression: %s\n", + expr_str_list[i].c_str()); + failure = true; + } + } + + if (failure) + return false; + } + + const std::size_t rounds = 100; + for (std::size_t r = 0; r < rounds; ++r) + { + T x = T(1); + T y = T(2); + T z = T(3); + T w = T(4); + T u = T(5); + T v = T(6); + + compositor_t fc; + + // f0() = 6 + fc.add("f0"," 3 * 2"); + + // f1(x) = 5 * (f0 + x) + fc.add("f1"," 5 * (f0+x)","x"); + + // f2(x,y) = 7 * (f1(x) + f1(y)) + fc.add("f2"," 7 * (f1(x)+f1(y))","x","y"); + + // f3(x,y,z) = 9 * (2(x,y) + f2(y,z) + f2(x,z)) + fc.add("f3"," 9 * (f2(x,y)+f2(y,z)+f2(x,z))","x","y","z"); + + // f4(x,y,z,w) = 11 * (f3(x,y,z) + f3(y,z,w) + f3(z,w,z)) + fc.add("f4","11 * (f3(x,y,z)+f3(y,z,w)+f3(z,w,x))","x","y","z","w"); + + // f5(x,y,z,w,u) = 13 * (f4(x,y,z,w) + f4(y,z,w,u) + f4(z,w,u,x) + f4(w,u,x,y)) + fc.add("f5","13 * (f4(x,y,z,w)+f4(y,z,w,u)+f4(z,w,u,x)+f4(w,u,x,y))","x","y","z","w","u"); + + // f6(x,y,z,w,u,v) = 17 * (f5(x,y,z,w,u) + f5(y,z,w,u,v) + f5(z,w,u,v,x) + f5(w,u,v,x,y)) + fc.add(function_t("f6") + .expression("17 * (f5(x,y,z,w,u)+f5(y,z,w,u,v)+f5(z,w,u,v,x)+f5(w,u,v,x,y))") + .var("x").var("y").var("z") + .var("w").var("u").var("v")); + + exprtk::symbol_table& symbol_table = fc.symbol_table(); + symbol_table.add_constants(); + symbol_table.add_variable("x",x); + symbol_table.add_variable("y",y); + symbol_table.add_variable("z",z); + symbol_table.add_variable("w",w); + symbol_table.add_variable("u",u); + symbol_table.add_variable("v",v); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + std::string expression_str = "f6(x,y,z,w,u,v) + 2"; + + if (!parser.compile(expression_str,expression)) + { + printf("run_test19() - Error: %s Expression: %s\n", + parser.error().c_str(), + expression_str.c_str()); + return false; + } + + T result = expression.value(); + + if (T(2122700582) != result) + { + printf("run_test19() - Error in evaluation! (2) Expression: %s\n", + expression_str.c_str()); + return false; + } + } + + return true; +} + int main() { #define perform_test(Type,Number) \ @@ -3381,6 +3538,7 @@ int main() perform_test(double,16) perform_test(double,17) perform_test(double,18) + perform_test(double,19) #undef perform_test