commit 992835d50af709655c73f7e2d9238bc899640c75 Author: Arash Partow Date: Sat Jan 28 05:25:39 2012 +0000 C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..27c687a --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +# +# ************************************************************** +# * C++ Mathematical Expression Toolkit Library * +# * * +# * Author: Arash Partow (1999-2012) * +# * URL: http://www.partow.net/programming/exprtk/index.html * +# * * +# * Copyright notice: * +# * Free use of the 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 * +# * * +# ************************************************************** +# + + +COMPILER = -c++ +#COMPILER = -clang +OPTIMIZATION_OPT = -O1 +BASE_OPTIONS = -ansi -pedantic-errors -Wall -Wextra -Werror -Wno-long-long +OPTIONS = $(BASE_OPTIONS) $(OPTIMIZATION_OPT) -o +LINKER_OPT = -L/usr/lib -lstdc++ + +BUILD_LIST+=exprtk_test + +all: $(BUILD_LIST) + +exprtk_test: exprtk_test.cpp exprtk.hpp + $(COMPILER) $(OPTIONS) exprtk_test exprtk_test.cpp $(LINKER_OPT) + + +pgo: exprtk_test.cpp exprtk.hpp + $(COMPILER) $(BASE_OPTIONS) -O3 -march=native -fprofile-generate -o exprtk_test exprtk_test.cpp $(LINKER_OPT) + +strip_bin: + strip -s exprtk_test + +valgrind_check: + valgrind --leak-check=full --show-reachable=yes --track-origins=yes ./exprtk_test + +clean: + rm -f core.* *~ *.o *.bak *stackdump gmon.out *.gcda *.gcno *.gcnor *.gch diff --git a/exprtk.hpp b/exprtk.hpp new file mode 100644 index 0000000..9ec4aa1 --- /dev/null +++ b/exprtk.hpp @@ -0,0 +1,3584 @@ +/* + ************************************************************** + * C++ Mathematical Expression Toolkit Library * + * * + * Author: Arash Partow (1999-2012) * + * URL: http://www.partow.net/programming/exprtk/index.html * + * * + * Copyright notice: * + * Free use of the 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) if(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) * + * * + ************************************************************** +*/ + + +#ifndef INCLUDE_EXPRTK_HPP +#define INCLUDE_EXPRTK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace exprtk +{ + namespace details + { + inline bool is_whitespace(const char& c) + { + return (' ' == c) || + ('\n' == c) || + ('\r' == c) || + ('\t' == c); + } + + inline bool is_operator_char(const char& c) + { + return ('*' == c) || ('/' == c) || + ('+' == c) || ('-' == c) || + ('^' == c) || ('<' == c) || + ('>' == c) || ('=' == c) || + (',' == c) || ('!' == c) || + ('(' == c) || (')' == c) || + ('[' == c) || (']' == c) || + ('{' == c) || ('}' == c) || + ('%' == c) || (':' == c) ; + } + + inline bool is_letter(const char c) + { + return (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z')); + } + + inline bool is_digit(const char c) + { + return ('0' <= c) && (c <= '9'); + } + + inline bool is_left_bracket(const char c) + { + return ('(' == c) || ('[' == c) || ('{' == c); + } + + inline bool is_right_bracket(const char c) + { + return (')' == c) || (']' == c) || ('}' == c); + } + + inline bool is_sign(const char c) + { + return ('+' == c) || ('-' == c); + } + + inline bool is_invalid(const char& c) + { + return !is_whitespace(c) && + !is_operator_char(c) && + !is_letter(c) && + !is_digit(c) && + ('.' != c) && + ('_' != c) && + ('$' != c); + } + + inline bool imatch(const std::string& s1, const std::string& s2) + { + if (s1.size() == s2.size()) + { + for (std::size_t i = 0; i < s1.size(); ++i) + { + if (std::tolower(s1[i]) != std::tolower(s2[i])) + { + return false; + } + } + return true; + } + else + return false; + } + + static const std::string reserved_symbols[] = + { + "abs", "acos", "and", "asin", "atan", "atan2", "avg", "ceil", "clamp", + "cos", "cosh", "cot", "csc", "deg2grad", "deg2rad", "equal", "exp", + "floor", "for", "grad2deg", "hyp", "inrange", "log", "log10", "logn", + "max", "min", "mod", "mul", "nand", "nor", "not", "not_equal", "or", + "rad2deg", "root", "round", "roundn", "sec", "shl", "shr", "sin", + "sinh", "sqrt", "sum", "tan", "tanh", "while", "xor" + }; + static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string); + + static const double pow10[] = { + 1.0, + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + }; + + namespace numeric + { + namespace constant + { + static const double e = 2.718281828459045235360; + static const double pi = 3.141592653589793238462; + static const double pi_2 = 1.570796326794896619231; + static const double pi_4 = 0.785398163397448309616; + static const double pi_180 = 0.017453292519943295769; + static const double _1_pi = 0.318309886183790671538; + static const double _2_pi = 0.636619772367581343076; + static const double _180_pi = 57.295779513082320876798; + } + + namespace details + { + struct unknown_type_tag {}; + struct real_type_tag {}; + struct int_type_tag {}; + + template + struct number_type { typedef unknown_type_tag type; }; + + #define exprtk_register_real_type_tag(T)\ + template<> struct number_type { typedef real_type_tag type; }; + + #define exprtk_register_int_type_tag(T)\ + template<> struct number_type { typedef int_type_tag type; }; + + exprtk_register_real_type_tag(double) + exprtk_register_real_type_tag(long double) + exprtk_register_real_type_tag(float) + exprtk_register_int_type_tag(short) + exprtk_register_int_type_tag(int) + exprtk_register_int_type_tag(long long int) + exprtk_register_int_type_tag(unsigned short) + exprtk_register_int_type_tag(unsigned int) + exprtk_register_int_type_tag(unsigned long long int) + + #undef exprtk_register_real_type_tag + #undef exprtk_register_real_type_tag + + template + inline T equal_impl(const T& v0, const T& v1, real_type_tag) + { + static const T epsilon = T(0.0000000001); + return (std::abs(v0 - v1) <= (std::max(T(1),std::max(std::abs(v0),std::abs(v1))) * epsilon)) ? T(1) : T(0); + } + + template + inline T equal_impl(const T& v0, const T& v1, int_type_tag) + { + return (v0 == v1) ? 1 : 0; + } + + template + inline T nequal_impl(const T& v0, const T& v1, real_type_tag) + { + static const T epsilon = T(0.0000000001); + return (std::abs(v0 - v1) > (std::max(T(1),std::max(std::abs(v0),std::abs(v1))) * epsilon)) ? T(1) : T(0); + } + + template + inline T nequal_impl(const T& v0, const T& v1, int_type_tag) + { + return (v0 != v1) ? 1 : 0; + } + + template + inline T modulus_impl(const T& v0, const T& v1, real_type_tag) + { + return std::fmod(v0,v1); + } + + template + inline T modulus_impl(const T& v0, const T& v1, int_type_tag) + { + return v0 % v1; + } + + template + inline T pow_impl(const T& v0, const T& v1, real_type_tag) + { + return std::pow(v0,v1); + } + + template + inline T pow_impl(const T& v0, const T& v1, int_type_tag) + { + return std::pow(static_cast(v0),static_cast(v1)); + } + + template + inline T logn_impl(const T& v0, const T& v1, real_type_tag) + { + return std::log(v0) / std::log(v1); + } + + template + inline T logn_impl(const T& v0, const T& v1, int_type_tag) + { + return static_cast(logn_impl(static_cast(v0),static_cast(v1),real_type_tag())); + } + + template + inline T root_impl(const T& v0, const T& v1, real_type_tag) + { + return std::pow(v0,T(1)/v1); + } + + template + inline T root_impl(const T& v0, const T& v1, int_type_tag) + { + return root_impl(static_cast(v0),static_cast(v1),real_type_tag()); + } + + template + inline T roundn_impl(const T& v0, const T& v1, real_type_tag) + { + return std::floor((v0 * pow10[(int)std::floor(v1)]) + T(0.5)) / T(pow10[(int)std::floor(v1)]); + } + + template + inline T roundn_impl(const T& v0, const T&, int_type_tag) + { + return v0; + } + + template + inline T hyp_impl(const T& v0, const T& v1, real_type_tag) + { + return std::sqrt((v0 * v0) + (v1 * v1)); + } + + template + inline T hyp_impl(const T& v0, const T& v1, int_type_tag) + { + return static_cast(std::sqrt(static_cast((v0 * v0) + (v1 * v1)))); + } + + template + inline T atan2_impl(const T& v0, const T& v1, real_type_tag) + { + return std::atan2(v0,v1); + } + + template + inline T atan2_impl(const T&, const T&, int_type_tag) + { + return 0; + } + + template + inline T shr_impl(const T& v0, const T& v1, real_type_tag) + { + return v0 * (T(1) / std::pow(T(2),static_cast(static_cast(v1)))); + } + + template + inline T shr_impl(const T& v0, const T& v1, int_type_tag) + { + return v0 >> v1; + } + + template + inline T shl_impl(const T& v0, const T& v1, real_type_tag) + { + return v0 * std::pow(T(2),static_cast(static_cast(v1))); + } + + template + inline T shl_impl(const T& v0, const T& v1, int_type_tag) + { + return v0 << v1; + } + + } + + template + inline T equal(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::equal_impl(v0,v1,num_type); + } + + template + inline T nequal(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::nequal_impl(v0,v1,num_type); + } + + template + inline T modulus(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::modulus_impl(v0,v1,num_type); + } + + template + inline T pow(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::pow_impl(v0,v1,num_type); + } + + template + inline T logn(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::logn_impl(v0,v1,num_type); + } + + template + inline T root(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::root_impl(v0,v1,num_type); + } + + template + inline T roundn(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::roundn_impl(v0,v1,num_type); + } + + template + inline T hyp(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::hyp_impl(v0,v1,num_type); + } + + template + inline T atan2(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::atan2_impl(v0,v1,num_type); + } + + template + inline T shr(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::shr_impl(v0,v1,num_type); + } + + template + inline T shl(const T& v0, const T& v1) + { + typename details::number_type::type num_type; + return details::shl_impl(v0,v1,num_type); + } + } + + template + struct token + { + + enum token_type + { + none = 0, + error = 1, + eof = 2, + number = 3, + symbol = 4, + assign = 5, + shr = 6, + shl = 7, + lte = 8, + ne = 9, + gte = 10, + lt = '<', + gt = '>', + eq = '=', + rbracket = ')', + lbracket = '(', + rsqrbracket = ']', + lsqrbracket = '[', + rcrlbracket = '}', + lcrlbracket = '{', + comma = ',', + add = '+', + sub = '-', + div = '/', + mul = '*', + mod = '%', + pow = '^' + }; + + token() {} + + token(token_type ttype, + const char* begin, const char* end, + const T& numeric_value = T(0)) + : type(ttype), + value(std::string(begin,end)), + numeric_value(numeric_value) + {} + + token_type type; + std::string value; + T numeric_value; + }; + + template + class lexer + { + public: + + typedef token token_t; + + inline bool process(const std::string& str) + { + error_description_ = ""; + s_itr = str.data(); + s_end = str.data() + str.size(); + eof_token_ = token_t(token_t::eof,s_end,s_end); + token_list_.clear(); + while (s_end != s_itr) + { + scan_token(); + if (!error_description_.empty()) + { + return false; + } + } + token_itr_ = token_list_.begin(); + store_token_itr_ = token_list_.begin(); + return true; + } + + inline void store() + { + store_token_itr_ = token_itr_; + } + + inline void restore() + { + token_itr_ = store_token_itr_; + } + + inline token_t& next_token() + { + if (token_list_.end() != token_itr_) + { + return *token_itr_++; + } + else + return eof_token_; + } + + inline std::string error() const + { + return error_description_; + } + + private: + + inline void scan_token() + { + while ((s_end != s_itr) && is_whitespace(*s_itr)) + { + ++s_itr; + } + + if (s_end == s_itr) + { + return; + } + else if (is_operator_char(*s_itr)) + { + if ((s_itr + 1) != s_end) + { + typename token_t::token_type ttype = token_t::none; + char c0 = s_itr[0]; + char c1 = s_itr[1]; + if ((c0 == '<') && (c1 == '=')) ttype = token_t::lte; + else if ((c0 == '>') && (c1 == '=')) ttype = token_t::gte; + else if ((c0 == '<') && (c1 == '>')) ttype = token_t::ne; + else if ((c0 == '!') && (c1 == '=')) ttype = token_t::ne; + else if ((c0 == '=') && (c1 == '=')) ttype = token_t::eq; + else if ((c0 == ':') && (c1 == '=')) ttype = token_t::assign; + else if ((c0 == '<') && (c1 == '-')) ttype = token_t::assign; + else if ((c0 == '<') && (c1 == '<')) ttype = token_t::shl; + else if ((c0 == '>') && (c1 == '>')) ttype = token_t::shr; + if (token_t::none != ttype) + { + token_list_.push_back(token_t(ttype,s_itr,s_itr + 2)); + s_itr += 2; + return; + } + } + if ('<' == *s_itr) + token_list_.push_back(token_t(token_t::lt,s_itr,s_itr + 1)); + else if ('>' == *s_itr) + token_list_.push_back(token_t(token_t::gt,s_itr,s_itr + 1)); + else + token_list_.push_back(token_t(typename token_t::token_type((*s_itr)),s_itr,s_itr + 1)); + ++s_itr; + return; + } + else if (is_letter(*s_itr)) + { + scan_symbol(); + return; + } + else if (is_digit((*s_itr)) || ('.' == (*s_itr))) + { + scan_number(); + return; + } + if ('$' == (*s_itr)) + { + scan_special_function(); + return; + } + else + { + set_error(std::string("scan_token() - error invalid token: ") + std::string(s_itr,s_itr + 2)); + token_list_.push_back(error(s_itr,s_itr + 1)); + ++s_itr; + } + } + + inline void scan_symbol() + { + const char* begin = s_itr; + while ((s_end != s_itr) && + (is_letter((*s_itr)) || is_digit ((*s_itr)) || ((*s_itr) == '_'))) + { + ++s_itr; + } + token_list_.push_back(token_t(token_t::symbol,begin,s_itr)); + } + + inline void scan_number() + { + /* + Attempt to match a valid numeric value in one of the following formats: + 1. 123456 + 2. 123.456 + 3. 123.456e3 + 4. 123.456E3 + 5. 123.456e+3 + 6. 123.456E+3 + 7. 123.456e-3 + 8. 123.456E-3 + */ + const char* begin = s_itr; + bool dot_found = false; + bool e_found = false; + bool post_e_sign_found = false; + while (s_end != s_itr) + { + if ('.' == (*s_itr)) + { + if (dot_found) + { + set_error(std::string("scan_number() - error invalid numeric token[1]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + dot_found = true; + ++s_itr; + continue; + } + else if (('e' == (*s_itr)) || ('E' == (*s_itr))) + { + const char& c = *(s_itr + 1); + if (s_end == (s_itr + 1)) + { + set_error(std::string("scan_number() - error invalid numeric token[2]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + else if (('+' != c) && ('-' != c) && !is_digit(c)) + { + set_error(std::string("scan_number() - error invalid numeric token[3]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + e_found = true; + ++s_itr; + continue; + } + else if (e_found && is_sign(*s_itr)) + { + if (post_e_sign_found) + { + set_error(std::string("scan_number() - error invalid numeric token[4]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + post_e_sign_found = true; + ++s_itr; + continue; + } + else if (('.' != (*s_itr)) && !is_digit(*s_itr)) + break; + else + ++s_itr; + } + T nval = T(atof(std::string(begin,s_itr).c_str())); + token_list_.push_back(token_t(token_t::number,begin,s_itr,nval)); + return; + } + + inline void scan_special_function() + { + const char* begin = s_itr; + //$fdd(x,x,x) = 11 chars + if (std::distance(s_itr,s_end) < 11) + { + set_error(std::string("scan_special_function() - error invalid special function [1]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + + if (!(('$' == *s_itr) && + ('f' == *(s_itr + 1)) && + ('(' == *(s_itr + 4)) && + (is_digit(*(s_itr + 2))) && + (is_digit(*(s_itr + 3))))) + { + set_error(std::string("scan_special_function() - error invalid special function [2]: ") + std::string(begin,s_itr)); + token_list_.push_back(error(begin,s_itr)); + return; + } + s_itr += 4; + token_list_.push_back(token_t(token_t::symbol,begin,s_itr)); + return; + } + + inline void set_error(const std::string& s) + { + if (error_description_.empty()) + { + error_description_ = s; + } + } + + inline token_t error(const char* begin, const char* end) + { + return token_t(token_t::error,begin,end); + } + + private: + + std::string error_description_; + std::deque token_list_; + typename std::deque::iterator token_itr_; + typename std::deque::iterator store_token_itr_; + token_t eof_token_; + const char* s_itr; + const char* s_end; + }; + + enum operator_type + { + e_default, + e_add , + e_sub , + e_mul , + e_div , + e_mod , + e_pow , + e_atan2 , + e_min , + e_max , + e_avg , + e_sum , + e_prod , + e_lt , + e_lte , + e_eq , + e_equal , + e_ne , + e_nequal , + e_gte , + e_gt , + e_and , + e_nand , + e_or , + e_nor , + e_xor , + e_shr , + e_shl , + e_abs , + e_acos , + e_asin , + e_atan , + e_ceil , + e_cos , + e_cosh , + e_exp , + e_floor , + e_log , + e_log10 , + 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_inrange, + e_r2d , + e_d2r , + e_d2g , + e_g2d , + e_hyp , + e_not , + e_assign , + + // Do not add new functions/operators after this point. + e_sf00 = 1000, + e_sf01 = 1001, + e_sf02 = 1002, + e_sf03 = 1003, + e_sf04 = 1004, + e_sf05 = 1005, + e_sf06 = 1006, + e_sf07 = 1007, + e_sf08 = 1008, + e_sf09 = 1009, + e_sf10 = 1010, + e_sf11 = 1011, + e_sf12 = 1012, + e_sf13 = 1013, + e_sf14 = 1014, + e_sf15 = 1015, + e_sf16 = 1016, + e_sf17 = 1017, + e_sf18 = 1018, + e_sf19 = 1019, + e_sf20 = 1020, + e_sf21 = 1021, + e_sf22 = 1022, + e_sf23 = 1023, + e_sf24 = 1024, + e_sf25 = 1025, + e_sf26 = 1026, + e_sf27 = 1027, + e_sf28 = 1028, + e_sf29 = 1029, + e_sf30 = 1030, + e_sf31 = 1031, + e_sf32 = 1032, + e_sf33 = 1033, + e_sf34 = 1034, + e_sf35 = 1035, + e_sf36 = 1036, + e_sf37 = 1037, + e_sf38 = 1038, + e_sf39 = 1039, + e_sf40 = 1040, + e_sf41 = 1041, + e_sf42 = 1042 + }; + + namespace numeric + { + namespace details + { + template + inline T process_impl(const operator_type operation, const T& arg, real_type_tag) + { + switch (operation) + { + case e_abs : return std::abs (arg); + case e_acos : return std::acos (arg); + case e_asin : return std::asin (arg); + case e_atan : return std::atan (arg); + case e_ceil : return std::ceil (arg); + case e_cos : return std::cos (arg); + case e_cosh : return std::cosh (arg); + case e_exp : return std::exp (arg); + case e_floor : return std::floor(arg); + case e_log : return std::log (arg); + case e_log10 : return std::log10(arg); + case e_neg : return -arg; + case e_pos : return +arg; + case e_round : return std::floor(arg + T(0.5)); + case e_sin : return std::sin (arg); + case e_sinh : return std::sinh (arg); + case e_sqrt : return std::sqrt (arg); + case e_tan : return std::tan (arg); + case e_tanh : return std::tanh (arg); + case e_cot : return T(1) / std::tan(arg); + case e_sec : return T(1) / std::cos(arg); + case e_csc : return T(1) / std::sin(arg); + case e_r2d : return (arg * T(numeric::constant::_180_pi)); + case e_d2r : return (arg * T(numeric::constant::pi_180)); + case e_d2g : return (arg * T(20/9)); + case e_g2d : return (arg * T(9/20)); + case e_not : return (arg != T(0) ? T(0) : T(1)); + default : return std::numeric_limits::infinity(); + } + } + + template + inline T process_impl(const operator_type operation, const T& arg, int_type_tag) + { + switch (operation) + { + case e_abs : return std::abs (arg); + case e_exp : return std::exp (arg); + case e_log : return std::log (arg); + case e_log10 : return std::log10(arg); + case e_neg : return -arg; + case e_pos : return +arg; + case e_sqrt : return std::sqrt (arg); + case e_not : return !arg; + default : return std::numeric_limits::infinity(); + } + } + + template + inline T process_impl(const operator_type operation, const T& arg0, const T& arg1, real_type_tag) + { + switch (operation) + { + case e_add : return (arg0 + arg1); + case e_sub : return (arg0 - arg1); + case e_mul : return (arg0 * arg1); + case e_div : return (arg0 / arg1); + case e_mod : return modulus(arg0,arg1); + case e_pow : return pow(arg0,arg1); + case e_atan2 : return atan2(arg0,arg1); + case e_min : return std::min(arg0,arg1); + case e_max : return std::max(arg0,arg1); + case e_logn : return logn(arg0,arg1); + case e_lt : return (arg0 < arg1) ? T(1) : T(0); + case e_lte : return (arg0 <= arg1) ? T(1) : T(0); + case e_eq : return (arg0 == arg1) ? T(1) : T(0); + case e_ne : return (arg0 != arg1) ? T(1) : T(0); + case e_gte : return (arg0 >= arg1) ? T(1) : T(0); + case e_gt : return (arg0 > arg1) ? T(1) : T(0); + case e_and : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(1) : T(0); + case e_nand : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(0) : T(1); + case e_or : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(1) : T(0); + case e_nor : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(0) : T(1); + case e_xor : return (arg0 != arg1) ? T(1) : T(0); + case e_root : return root(arg0,arg1); + case e_roundn : return roundn(arg0,arg1); + case e_equal : return equal(arg0,arg1); + case e_nequal : return nequal(arg0,arg1); + case e_hyp : return hyp(arg0,arg1); + case e_avg : return (arg0 + arg1)/T(2); + case e_sum : return (arg0 + arg1); + case e_prod : return (arg0 * arg1); + case e_shr : return shr(arg0,arg1); + case e_shl : return shl(arg0,arg1); + default : return std::numeric_limits::infinity(); + } + } + + template + inline T process_impl(const operator_type operation, const T& arg0, const T& arg1, int_type_tag) + { + switch (operation) + { + case e_add : return (arg0 + arg1); + case e_sub : return (arg0 - arg1); + case e_mul : return (arg0 * arg1); + case e_div : return (arg0 / arg1); + case e_mod : return arg0 % arg1; + case e_pow : return pow(arg0,arg1); + case e_min : return std::min(arg0,arg1); + case e_max : return std::max(arg0,arg1); + case e_logn : return logn(arg0,arg1); + case e_lt : return (arg0 < arg1) ? T(1) : T(0); + case e_lte : return (arg0 <= arg1) ? T(1) : T(0); + case e_eq : return (arg0 == arg1) ? T(1) : T(0); + case e_ne : return (arg0 != arg1) ? T(1) : T(0); + case e_gte : return (arg0 >= arg1) ? T(1) : T(0); + case e_gt : return (arg0 > arg1) ? T(1) : T(0); + case e_and : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(1) : T(0); + case e_nand : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(0) : T(1); + case e_or : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(1) : T(0); + case e_nor : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(0) : T(1); + case e_xor : return (arg0 != arg1) ? T(1) : T(0); + case e_root : return root(arg0,arg1); + case e_equal : return arg0 == arg1; + case e_nequal : return arg0 != arg1; + case e_hyp : return hyp(arg0,arg1); + case e_avg : return (arg0 + arg1) >> 1; + case e_sum : return (arg0 + arg1); + case e_prod : return (arg0 * arg1); + case e_shr : return arg0 >> arg1; + case e_shl : return arg0 << arg1; + default : return std::numeric_limits::infinity(); + } + } + } + + template + inline T process(const operator_type operation, const T& arg) + { + typename details::number_type::type num_type; + return details::process_impl(operation,arg,num_type); + } + + template + inline T process(const operator_type operation, const T& arg0, const T& arg1) + { + typename details::number_type::type num_type; + return details::process_impl(operation,arg0,arg1,num_type); + } + } + + template + class expression_node + { + public: + + enum node_type + { + e_none , + e_constant , + e_unary , + e_binary , + e_trinary , + e_quaternary , + e_quinary , + e_senary , + e_conditional , + e_while , + e_variable , + e_function + }; + + typedef T value_type; + typedef expression_node* expression_ptr; + + virtual ~expression_node() + {} + + virtual inline T value() + { + return std::numeric_limits::quiet_NaN(); + } + + virtual inline node_type type() const + { + return e_none; + } + }; + + template + inline bool branch_deletable(expression_node* expr) + { + return expression_node::e_variable != expr->type(); + } + + template + class literal_node : public expression_node + { + public: + + explicit literal_node(const T& value) + : value_(value) + {} + + inline T value() + { + return value_; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_constant; + } + + private: + + T value_; + }; + + template + class unary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + unary_node(const operator_type& operation, + expression_ptr branch) + : operation_(operation), + branch_(branch), + branch_deletable_(branch_deletable(branch_)) + {} + + ~unary_node() + { + if (branch_ && branch_deletable_) { delete branch_; branch_ = 0; } + } + + inline T value() + { + const T arg = branch_->value(); + return numeric::process(operation_,arg); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_unary; + } + + private: + + operator_type operation_; + expression_ptr branch_; + bool branch_deletable_; + }; + + template + inline void init_branches(std::pair< expression_node*,bool> (&branch)[N], + expression_node* b0, + expression_node* b1 = reinterpret_cast*>(0), + expression_node* b2 = reinterpret_cast*>(0), + expression_node* b3 = reinterpret_cast*>(0), + expression_node* b4 = reinterpret_cast*>(0), + expression_node* b5 = reinterpret_cast*>(0), + expression_node* b6 = reinterpret_cast*>(0), + expression_node* b7 = reinterpret_cast*>(0), + expression_node* b8 = reinterpret_cast*>(0), + expression_node* b9 = reinterpret_cast*>(0)) + { + typedef expression_node Node; + //Needed for incompetent and broken msvc compiler versions + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4127) + #endif + if (b0 && (N > 0)) { branch[0] = std::make_pair(b0,branch_deletable(b0)); } + if (b1 && (N > 1)) { branch[1] = std::make_pair(b1,branch_deletable(b1)); } + if (b2 && (N > 2)) { branch[2] = std::make_pair(b2,branch_deletable(b2)); } + if (b3 && (N > 3)) { branch[3] = std::make_pair(b3,branch_deletable(b3)); } + if (b4 && (N > 4)) { branch[4] = std::make_pair(b4,branch_deletable(b4)); } + if (b5 && (N > 5)) { branch[5] = std::make_pair(b5,branch_deletable(b5)); } + if (b6 && (N > 6)) { branch[6] = std::make_pair(b6,branch_deletable(b6)); } + if (b7 && (N > 7)) { branch[7] = std::make_pair(b7,branch_deletable(b7)); } + if (b8 && (N > 8)) { branch[8] = std::make_pair(b8,branch_deletable(b8)); } + if (b9 && (N > 9)) { branch[9] = std::make_pair(b9,branch_deletable(b9)); } + #ifdef _MSC_VER + #pragma warning(pop) + #endif + } + + template + inline void cleanup_branches(std::pair*,bool> (&branch)[N]) + { + for (std::size_t i = 0; i < N; ++i) + { + if (branch[i].first && branch[i].second) + { + delete branch[i].first; + branch[i].first = 0; + } + } + } + + template + class binary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + binary_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1) + : operation_(operation) + { + init_branches<2>(branch_,branch0,branch1); + } + + ~binary_node() + { + cleanup_branches<2>(branch_); + } + + inline T value() + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + return numeric::process(operation_,arg0,arg1); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_binary; + } + + protected: + + operator_type operation_; + branch_t branch_[2]; + }; + + template + class trinary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + trinary_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2) + : operation_(operation) + { + init_branches<3>(branch_,branch0,branch1,branch2); + } + + ~trinary_node() + { + cleanup_branches<3>(branch_); + } + + inline T value() + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + const T arg2 = branch_[2].first->value(); + switch (operation_) + { + case e_clamp : return (arg1 < arg0) ? arg0 : (arg1 > arg2 ? arg2 : arg1); + case e_inrange : return (arg1 < arg0) ? T(0) : ((arg1 > arg2) ? T(0) : T(1)); + case e_min : return std::min(std::min(arg0,arg1),arg2); + case e_max : return std::max(std::max(arg0,arg1),arg2); + case e_avg : return (arg0 + arg1 + arg2) / T(3.0); + case e_sum : return (arg0 + arg1 + arg2); + case e_prod : return (arg0 * arg1 * arg2); + default : return std::numeric_limits::infinity(); + } + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_trinary; + } + + protected: + + operator_type operation_; + branch_t branch_[3]; + }; + + template + class quaternary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + quaternary_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3) + : operation_(operation) + { + init_branches<4>(branch_,branch0,branch1,branch2,branch3); + } + + ~quaternary_node() + { + cleanup_branches<4>(branch_); + } + + inline T value() + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + const T arg2 = branch_[2].first->value(); + const T arg3 = branch_[3].first->value(); + switch (operation_) + { + case e_min : return std::min(std::min(arg0,arg1),std::min(arg2,arg3)); + case e_max : return std::max(std::max(arg0,arg1),std::max(arg2,arg3)); + case e_avg : return (arg0 + arg1 + arg2 + arg3) / T(4.0); + case e_sum : return (arg0 + arg1 + arg2 + arg3); + case e_prod : return (arg0 * arg1 * arg2 * arg3); + default : return std::numeric_limits::infinity(); + } + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_quaternary; + } + + protected: + + operator_type operation_; + branch_t branch_[4]; + }; + + template + class quinary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + quinary_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3, + expression_ptr branch4) + : operation_(operation) + { + init_branches<5>(branch_,branch0,branch1,branch2,branch3,branch4); + } + + ~quinary_node() + { + cleanup_branches<5>(branch_); + } + + inline T value() + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + const T arg2 = branch_[2].first->value(); + const T arg3 = branch_[3].first->value(); + const T arg4 = branch_[4].first->value(); + switch (operation_) + { + case e_min : return std::min(std::min(std::min(arg0,arg1),std::min(arg2,arg3)),arg4); + case e_max : return std::max(std::max(std::max(arg0,arg1),std::max(arg2,arg3)),arg4); + case e_avg : return (arg0 + arg1 + arg2 + arg3 + arg4) / T(5.0); + case e_sum : return (arg0 + arg1 + arg2 + arg3 + arg4); + case e_prod : return (arg0 * arg1 * arg2 * arg3 * arg4); + default : return std::numeric_limits::infinity(); + } + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_quinary; + } + + private: + + operator_type operation_; + branch_t branch_[5]; + }; + + template + class senary_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + senary_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3, + expression_ptr branch4, + expression_ptr branch5) + : operation_(operation) + { + init_branches<6>(branch_,branch0,branch1,branch2,branch3,branch4,branch5); + } + + ~senary_node() + { + cleanup_branches<6>(branch_); + } + + inline T value() + { + const T arg0 = branch_[0].first->value(); + const T arg1 = branch_[1].first->value(); + const T arg2 = branch_[2].first->value(); + const T arg3 = branch_[3].first->value(); + const T arg4 = branch_[4].first->value(); + const T arg5 = branch_[5].first->value(); + switch (operation_) + { + case e_min : return std::min(std::min(std::min(arg0,arg1),std::min(arg2,arg3)),std::min(arg4,arg5)); + case e_max : return std::max(std::max(std::max(arg0,arg1),std::max(arg2,arg3)),std::max(arg4,arg5)); + case e_avg : return (arg0 + arg1 + arg2 + arg3 + arg4 + arg5) / T(6.0); + case e_sum : return (arg0 + arg1 + arg2 + arg3 + arg4 + arg5); + case e_prod : return (arg0 * arg1 * arg2 * arg3 * arg4 * arg5); + case e_default : + default : return std::numeric_limits::infinity(); + } + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_senary; + } + + private: + + operator_type operation_; + branch_t branch_[6]; + }; + + template + class conditional_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + conditional_node(expression_ptr test, + expression_ptr consequent, + expression_ptr alternative) + : test_(test), + consequent_(consequent), + alternative_(alternative), + test_deletable_(expression_node::e_variable != test_->type()), + consequent_deletable_(expression_node::e_variable != consequent_->type()), + alternative_deletable_(expression_node::e_variable != alternative_->type()) + {} + + ~conditional_node() + { + if (test_ && test_deletable_) delete test_; + if (consequent_ && consequent_deletable_) delete consequent_; + if (alternative_ && alternative_deletable_) delete alternative_; + } + + inline T value() + { + if (test_->value() != 0) + return consequent_->value(); + else + return alternative_->value(); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_conditional; + } + + private: + + expression_ptr test_; + expression_ptr consequent_; + expression_ptr alternative_; + bool test_deletable_; + bool consequent_deletable_; + bool alternative_deletable_; + }; + + template + class while_loop_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + while_loop_node(expression_ptr test, + expression_ptr branch) + : test_(test), + branch_(branch), + test_deletable_(expression_node::e_variable != test_->type()), + branch_deletable_(expression_node::e_variable != branch_->type()) + {} + + ~while_loop_node() + { + if (test_ && test_deletable_) delete test_; + if (branch_ && branch_deletable_) delete branch_; + } + + inline T value() + { + T result = T(0); + while (test_->value() != T(0)) + { + result = branch_->value(); + } + return result; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_while; + } + + private: + + expression_ptr test_; + expression_ptr branch_; + bool test_deletable_; + bool branch_deletable_; + }; + + template + class variable_node : public expression_node + { + public: + + static T null_value; + + explicit variable_node() + : value_(&null_value) + {} + + explicit variable_node(T& value) + : value_(&value) + {} + + inline bool operator <(const variable_node& v) const + { + return this < (&v); + } + + inline T value() + { + return (*value_); + } + + inline T& ref() + { + return (*value_); + } + + inline const T& ref() const + { + return (*value_); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_variable; + } + + private: + + T* value_; + }; + + template + T variable_node::null_value = T(std::numeric_limits::infinity()); + + template + class sf3_node : public trinary_node + { + public: + + typedef expression_node* expression_ptr; + + sf3_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2) + : trinary_node(operation,branch0,branch1,branch2) + {} + + inline T value() + { + const T x = trinary_node::branch_[0].first->value(); + const T y = trinary_node::branch_[1].first->value(); + const T z = trinary_node::branch_[2].first->value(); + switch (trinary_node::operation_) + { + case e_sf00 : return (x + y) / z; + case e_sf01 : return (x + y) * z; + case e_sf02 : return (x - y) / z; + case e_sf03 : return (x - y) * z; + case e_sf04 : return (x * y) + z; + case e_sf05 : return (x * y) - z; + case e_sf06 : return (x * y) / z; + case e_sf07 : return (x * y) * z; + case e_sf08 : return (x / y) + z; + case e_sf09 : return (x / y) - z; + case e_sf10 : return (x / y) / z; + case e_sf11 : return (x / y) * z; + case e_sf12 : return z / (x + y); + case e_sf13 : return z / (x - y); + case e_sf14 : return z / (x * y); + case e_sf15 : return z / (x / y); + case e_sf16 : return z - (x / y); + case e_sf17 : return z - (x / y); + default : return std::numeric_limits::infinity(); + } + } + }; + + template + class sf4_node : public quaternary_node + { + public: + + typedef expression_node* expression_ptr; + + sf4_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1, + expression_ptr branch2, + expression_ptr branch3) + : quaternary_node(operation,branch0,branch1,branch2,branch3) + {} + + inline T value() + { + const T x = quaternary_node::branch_[0].first->value(); + const T y = quaternary_node::branch_[1].first->value(); + const T z = quaternary_node::branch_[2].first->value(); + const T w = quaternary_node::branch_[3].first->value(); + switch (quaternary_node::operation_) + { + case e_sf18 : return w + ((x + y) / z); + case e_sf19 : return w + ((x + y) * z); + case e_sf20 : return w + ((x - y) / z); + case e_sf21 : return w + ((x - y) * z); + case e_sf22 : return w + ((x * y) / z); + case e_sf23 : return w + ((x * y) * z); + case e_sf24 : return w + ((x / y) + z); + case e_sf25 : return w + ((x / y) / z); + case e_sf26 : return w + ((x / y) * z); + case e_sf27 : return w - ((x + y) / z); + case e_sf28 : return w - ((x + y) * z); + case e_sf29 : return w - ((x - y) / z); + case e_sf30 : return w - ((x - y) * z); + case e_sf31 : return w - ((x * y) / z); + case e_sf32 : return w - ((x * y) * z); + case e_sf33 : return w - ((x / y) / z); + case e_sf34 : return w - ((x / y) * z); + case e_sf35 : return ((x + y) * z) - w; + case e_sf36 : return ((x - y) * z) - w; + case e_sf37 : return ((x * y) * z) - w; + case e_sf38 : return ((x / y) * z) - w; + case e_sf39 : return ((x + y) / z) - w; + case e_sf40 : return ((x - y) / z) - w; + case e_sf41 : return ((x * y) / z) - w; + case e_sf42 : return ((x / y) / z) - w; + default : return std::numeric_limits::infinity(); + } + } + }; + + template + class assignment_node : public binary_node + { + public: + + typedef expression_node* expression_ptr; + + assignment_node(const operator_type& operation, + expression_ptr branch0, + expression_ptr branch1) + : binary_node(operation,branch0,branch1) + {} + + inline T value() + { + variable_node* var_node_ptr = dynamic_cast*>(binary_node::branch_[0].first); + if (var_node_ptr) + { + T& result = dynamic_cast*>(binary_node::branch_[0].first)->ref(); + result = binary_node::branch_[1].first->value(); + return result; + } + else + return std::numeric_limits::infinity(); + } + }; + + template + class function_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + typedef IFunction ifunction; + + static const std::size_t N = 10; + + function_node(ifunction* func) + : function(func) + {} + + ~function_node() + { + cleanup_branches(branch_); + } + + + template + void init_branches(expression_ptr (&b)[NumBranches]) + { + for (std::size_t i = 0; i < NumBranches; ++i) + { + if (b[i]) + { + branch_[i] = std::make_pair(b[i],branch_deletable(b[i])); + } + } + } + + inline bool operator <(const function_node& fn) const + { + return this < (&fn); + } + + inline T value() + { + T v[N]; + if (function->param_count) + { + for (std::size_t i = 0; i < function->param_count; ++i) + { + if (v[i]) + v[i] = branch_[i].first->value(); + else + std::numeric_limits::infinity(); + } + } + switch (function->param_count) + { + case 0 : return (*function)(); + case 1 : return (*function)(v[0]); + case 2 : return (*function)(v[0],v[1]); + case 3 : return (*function)(v[0],v[1],v[2]); + case 4 : return (*function)(v[0],v[1],v[2],v[3]); + case 5 : return (*function)(v[0],v[1],v[2],v[3],v[4]); + case 6 : return (*function)(v[0],v[1],v[2],v[3],v[4],v[5]); + case 7 : return (*function)(v[0],v[1],v[2],v[3],v[4],v[5],v[6]); + case 8 : return (*function)(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]); + case 9 : return (*function)(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8]); + case 10 : return (*function)(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9]); + default : return std::numeric_limits::infinity(); + } + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_function; + } + + private: + + ifunction* function; + branch_t branch_[N]; + }; + + class node_allocator + { + public: + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[1]) + { + return allocate(operation,branch[0]); + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[2]) + { + return allocate(operation,branch[0],branch[1]); + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[3]) + { + return allocate(operation,branch[0],branch[1],branch[2]); + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[4]) + { + return allocate(operation,branch[0],branch[1],branch[2],branch[3]); + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[5]) + { + return allocate(operation,branch[0],branch[1],branch[2],branch[3],branch[4]); + } + + template + inline expression_node* allocate(OpType& operation, ExprNode (&branch)[6]) + { + return allocate(operation,branch[0],branch[1],branch[2],branch[3],branch[4],branch[5]); + } + + template + inline expression_node* allocate(T1& t1) const + { + return new node_type(t1); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2) const + { + return new node_type(t1,t2); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3) const + { + return new node_type(t1,t2,t3); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4) const + { + return new node_type(t1,t2,t3,t4); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5) const + { + return new node_type(t1,t2,t3,t4,t5); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6) const + { + return new node_type(t1,t2,t3,t4,t5,t6); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7) const + { + return new node_type(t1,t2,t3,t4,t5,t6,t7); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8) const + { + return new node_type(t1,t2,t3,t4,t5,t6,t7,t8); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8, + const T9& t9) const + { + return new node_type(t1,t2,t3,t4,t5,t6,t7,t8,t9); + } + + template + inline expression_node* allocate(const T1& t1, const T2& t2, + const T3& t3, const T4& t4, + const T5& t5, const T6& t6, + const T7& t7, const T8& t8, + const T9& t9, const T10& t10) const + { + return new node_type(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10); + } + + template + void inline free(expression_node*& e) const + { + delete e; + e = 0; + } + }; + + struct operation_t + { + operation_t(const std::string& n, const operator_type t, const unsigned int& np) + : name(n), + type(t), + num_params(np) + {} + + std::string name; + operator_type type; + unsigned int num_params; + }; + + static const operation_t operation_list[] = + { + operation_t( "abs" , e_abs , 1), + operation_t( "acos" , e_acos , 1), + operation_t( "asin" , e_asin , 1), + operation_t( "atan" , e_atan , 1), + operation_t( "ceil" , e_ceil , 1), + operation_t( "cos" , e_cos , 1), + operation_t( "cosh" , e_cosh , 1), + operation_t( "exp" , e_exp , 1), + operation_t( "floor" , e_floor , 1), + operation_t( "log" , e_log , 1), + operation_t( "log10" , e_log10 , 1), + operation_t( "round" , e_round , 1), + operation_t( "sin" , e_sin , 1), + operation_t( "sinh" , e_sinh , 1), + operation_t( "sec" , e_sec , 1), + operation_t( "csc" , e_csc , 1), + operation_t( "sqrt" , e_sqrt , 1), + operation_t( "tan" , e_tan , 1), + operation_t( "tanh" , e_tanh , 1), + operation_t( "cot" , e_cot , 1), + operation_t( "rad2deg" , e_r2d , 1), + operation_t( "deg2rad" , e_d2r , 1), + operation_t( "deg2grad" , e_d2g , 1), + operation_t( "grad2deg" , e_g2d , 1), + operation_t( "not" , e_not , 1), + operation_t( "atan2", e_atan2 , 2), + operation_t( "min", e_min , 2), + operation_t( "max", e_max , 2), + operation_t( "avg", e_avg , 2), + operation_t( "sum", e_sum , 2), + operation_t( "mul", e_prod , 2), + operation_t( "mod", e_mod , 2), + operation_t( "logn", e_logn , 2), + operation_t( "root", e_root , 2), + operation_t( "roundn", e_roundn , 2), + operation_t( "equal", e_equal , 2), + operation_t("not_equal", e_nequal , 2), + operation_t( "hyp", e_hyp , 2), + operation_t( "shr", e_shr , 2), + operation_t( "shl", e_shl , 2), + operation_t( "clamp", e_clamp , 3), + operation_t( "inrange", e_inrange , 3), + operation_t( "min", e_min , 3), + operation_t( "max", e_max , 3), + operation_t( "avg", e_avg , 3), + operation_t( "sum", e_sum , 3), + operation_t( "mul", e_prod , 3), + operation_t( "min", e_min , 4), + operation_t( "max", e_max , 4), + operation_t( "avg", e_avg , 4), + operation_t( "sum", e_sum , 4), + operation_t( "mul", e_prod , 4), + operation_t( "min", e_min , 5), + operation_t( "max", e_max , 5), + operation_t( "avg", e_avg , 5), + operation_t( "sum", e_sum , 5), + operation_t( "mul", e_prod , 5), + operation_t( "min", e_min , 6), + operation_t( "max", e_max , 6), + operation_t( "avg", e_avg , 6), + operation_t( "sum", e_sum , 6), + operation_t( "mul", e_prod , 6), + }; + + static const std::size_t operation_list_size = sizeof(operation_list) / sizeof(operation_t); + + } // namespace details + + template + class ifunction + { + public: + + explicit ifunction(const std::size_t& pc) + : param_count(pc) + {} + + virtual ~ifunction() + {} + + std::size_t param_count; + + inline virtual T operator()() + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&,const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + + inline virtual T operator()(const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) + { + return std::numeric_limits::infinity(); + } + }; + + template + class symbol_table + { + private: + + struct ilesscompare + { + inline bool operator()(const std::string& s1, const std::string& s2) const + { + const std::size_t length = std::min(s1.size(),s2.size()); + for (std::size_t i = 0; i < length; ++i) + { + if (std::tolower(s1[i]) > std::tolower(s2[i])) + return false; + else if (std::tolower(s1[i]) < std::tolower(s2[i])) + return true; + } + return s1.size() < s2.size(); + } + }; + + typedef typename details::variable_node variable_t; + typedef variable_t* variable_ptr; + typedef std::pair variable_pair_t; + typedef std::map variable_map_t; + typedef typename variable_map_t::iterator vm_itr_t; + typedef typename variable_map_t::const_iterator vm_const_itr_t; + + typedef ifunction function_t; + typedef function_t* function_ptr; + typedef std::map function_map_t; + typedef typename function_map_t::iterator fm_itr_t; + typedef typename function_map_t::const_iterator fm_const_itr_t; + + static const std::size_t lut_size = 256; + + public: + + symbol_table() + : variable_count_(0), + function_count_(0) + { + clear_short_symbol_luts(); + } + + ~symbol_table() + { + if (!variable_map_.empty()) + { + vm_itr_t itr = variable_map_.begin(); + vm_itr_t end = variable_map_.end(); + while (end != itr) + { + delete (*itr).second.second; + ++itr; + } + variable_map_.clear(); + } + if (!function_map_.empty()) + { + function_map_.clear(); + } + for (std::size_t i = 0; i < lut_size; ++i) + { + if (short_variable_lut_[i].second) delete short_variable_lut_[i].second; + if (short_function_lut_[i]) delete short_function_lut_[i]; + } + clear_short_symbol_luts(); + } + + inline std::size_t variable_count() + { + return variable_count_; + } + + inline std::size_t function_count() + { + return function_count_; + } + + inline variable_ptr get_variable(const std::string& variable_name) + { + if (!valid_symbol(variable_name)) + return reinterpret_cast(0); + else if (1 == variable_name.size()) + { + variable_pair_t& vp = short_variable_lut_[static_cast(variable_name[0])]; + if (vp.second) + return vp.second; + else + return reinterpret_cast(0); + } + else + { + vm_const_itr_t itr = variable_map_.find(variable_name); + if (variable_map_.end() == itr) + return reinterpret_cast(0); + else + return itr->second.second; + } + } + + inline function_ptr get_function(const std::string& function_name) + { + if (!valid_symbol(function_name)) + return reinterpret_cast(0); + else if (1 == function_name.size()) + { + return short_function_lut_[static_cast(function_name[0])]; + } + else + { + fm_const_itr_t itr = function_map_.find(function_name); + if (function_map_.end() == itr) + return reinterpret_cast(0); + else + return itr->second; + } + } + + inline T& variable_ref(const std::string& symbol_name) + { + static T null_var = T(0); + if (!valid_symbol(symbol_name)) + return null_var; + else if (1 == symbol_name.size()) + { + variable_pair_t& vp = short_variable_lut_[static_cast(symbol_name[0])]; + if (vp.second) + return vp->second.ref(); + else + return null_var; + } + else + { + vm_const_itr_t itr = variable_map_.find(symbol_name); + if (variable_map_.end() == itr) + return null_var; + else + return itr->second.second->ref(); + } + } + + inline bool is_constant(const std::string& symbol_name) const + { + if (1 == symbol_name.size()) + { + return short_variable_lut_[static_cast(symbol_name[0])].first; + } + else + { + vm_const_itr_t itr = variable_map_.find(symbol_name); + if (variable_map_.end() == itr) + return false; + else + return itr->second.first; + } + } + + inline bool add_variable(const std::string& variable_name, T& t, const bool is_constant = false) + { + if (!valid_symbol(variable_name)) + return false; + else if (symbol_exits(variable_name)) + return false; + else if (1 == variable_name.size()) + { + variable_pair_t& vp = short_variable_lut_[static_cast(variable_name[0])]; + vp.first = is_constant; + vp.second = new variable_t(t); + ++variable_count_; + } + else + { + for (std::size_t i = 0; i < details::reserved_symbols_size; ++i) + { + if (details::imatch(variable_name,details::reserved_symbols[i])) + { + return false; + } + } + vm_itr_t itr = variable_map_.find(variable_name); + if (variable_map_.end() == itr) + { + variable_map_[variable_name] = std::make_pair(is_constant,new details::variable_node(t)); + ++variable_count_; + } + } + return true; + } + + inline bool add_function(const std::string& function_name, function_t& function) + { + if (!valid_symbol(function_name)) + return false; + else if (symbol_exits(function_name)) + return false; + else if (1 == function_name.size()) + { + short_function_lut_[static_cast(function_name[0])] = &function; + ++function_count_; + } + else + { + for (std::size_t i = 0; i < details::reserved_symbols_size; ++i) + { + if (details::imatch(function_name,details::reserved_symbols[i])) + { + return false; + } + } + fm_itr_t itr = function_map_.find(function_name); + if (function_map_.end() == itr) + { + function_map_[function_name] = &function; + ++function_count_; + } + else + return false; + } + return true; + } + + inline bool remove_variable(const std::string& variable_name) + { + if (1 == variable_name.size()) + { + variable_pair_t& vp = short_variable_lut_[static_cast(variable_name[0])]; + if (0 == vp.second) + return false; + delete vp.second; + vp.first = false; + vp.second = 0; + --variable_count_; + return true; + } + else + { + vm_itr_t itr = variable_map_.find(variable_name); + if (variable_map_.end() != itr) + { + delete itr->second; + variable_map_.erase(itr); + --variable_count_; + return true; + } + else + return false; + } + } + + inline bool remove_function(const std::string& function_name) + { + if (1 == function_name.size()) + { + if (short_function_lut_[static_cast(function_name[0])]) + { + short_function_lut_[static_cast(function_name[0])] = 0; + --function_count_; + return true; + } + else + return false; + } + else + { + vm_itr_t itr = function_map_.find(function_name); + if (function_map_.end() != itr) + { + function_map_.erase(itr); + --function_count_; + return true; + } + else + return false; + } + } + + inline void add_constants() + { + add_pi(); + add_epsilon(); + add_infinity(); + } + + inline void add_pi() + { + static T pi = T(details::numeric::constant::pi); + add_variable("pi",pi,true); + } + + inline void add_epsilon() + { + static T epsilon = std::numeric_limits::epsilon(); + add_variable("epsilon",epsilon,true); + } + + inline void add_infinity() + { + static T infinity = std::numeric_limits::infinity(); + add_variable("inf",infinity,true); + } + + template class Sequence> + inline std::size_t get_variable_list(Sequence,Allocator>& vlist) + { + std::size_t count = 0; + for (std::size_t i = 0; i < lut_size; ++i) + { + variable_pair_t& vp = short_variable_lut_[static_cast(i)]; + if (0 != vp.second) + { + vlist.push_back(std::make_pair(std::string("")+static_cast(i),vp.second->value())); + ++count; + } + } + if (!variable_map_.empty()) + { + vm_itr_t itr = variable_map_.begin(); + vm_itr_t end = variable_map_.end(); + while (end != itr) + { + vlist.push_back(std::make_pair((*itr).first,itr->second.second->ref())); + ++itr; + ++count; + } + } + return count; + } + + inline bool symbol_exits(const std::string& symbol_name) const + { + /* + Will return true if either a variable or function + name has already been defined in any of the LUTs + or maps. + */ + if ((1 == symbol_name.size()) && short_variable_lut_[static_cast(symbol_name[0])].second) + return true; + else if ((1 == symbol_name.size()) && short_function_lut_[static_cast(symbol_name[0])]) + return true; + else if (variable_map_.end() != variable_map_.find(symbol_name)) + return true; + else if (function_map_.end() != function_map_.find(symbol_name)) + return true; + else + return false; + } + + private: + + inline bool valid_symbol(const std::string& symbol) const + { + if (symbol.empty()) + return false; + if (!details::is_letter(symbol[0])) + return false; + else if (symbol.size() > 1) + { + for (std::size_t i = 1; i < symbol.size(); ++i) + { + if ( + (!details::is_letter(symbol[i])) && + (!details:: is_digit(symbol[i])) && + ('_' != symbol[i]) + ) + return false; + } + } + return true; + } + + inline void clear_short_symbol_luts() + { + for (std::size_t i = 0; i < lut_size; ++i) + { + short_variable_lut_[i].first = false; + short_variable_lut_[i].second = reinterpret_cast(0); + short_function_lut_[i] = reinterpret_cast(0); + } + } + + variable_pair_t short_variable_lut_[lut_size]; + variable_map_t variable_map_; + + function_ptr short_function_lut_[lut_size]; + function_map_t function_map_; + + std::size_t variable_count_; + std::size_t function_count_; + }; + + template class parser; + + template + class expression + { + private: + + typedef details::expression_node* expression_ptr; + typedef T type; + + struct expression_holder + { + expression_holder() + : ref_count(0), + expr(0) + {} + + expression_holder(expression_ptr e) + : ref_count(1), + expr(e) + {} + + ~expression_holder() + { + if (expr && details::expression_node::e_variable != expr->type()) + { + delete expr; + } + } + + std::size_t ref_count; + expression_ptr expr; + }; + + public: + + expression() + : expression_holder_(0), + symbol_table_(0) + {} + + expression(const expression& e) + { + expression_holder_ = e.expression_holder_; + expression_holder_->ref_count++; + symbol_table_ = e.symbol_table_; + } + + expression& operator=(const expression& e) + { + if (expression_holder_) + { + if (0 == --expression_holder_->ref_count) + { + delete expression_holder_; + } + expression_holder_ = 0; + } + expression_holder_ = e.expression_holder_; + expression_holder_->ref_count++; + symbol_table_ = e.symbol_table_; + return *this; + } + + inline bool operator!() + { + return ((0 == expression_holder_) || (0 == expression_holder_->expr)); + } + + ~expression() + { + if (expression_holder_) + { + if (0 == --expression_holder_->ref_count) + { + if (details::expression_node::e_variable != expression_holder_->expr->type()) + { + delete expression_holder_; + } + } + } + } + + inline T operator()() + { + return value(); + } + + inline T value() + { + if (expression_holder_ && expression_holder_->expr) + return expression_holder_->expr->value(); + else + return std::numeric_limits::quiet_NaN(); + } + + inline operator T() + { + return value(); + } + + inline operator bool() + { + return (T(0) != value()); + } + + inline void register_symbol_table(symbol_table& st) + { + symbol_table_ = &st; + } + + inline symbol_table* get_symbol_table() + { + return symbol_table_; + } + + private: + + inline void set_expression(const expression_ptr expr) + { + if (expr) + { + if (expression_holder_) + { + if (0 == --expression_holder_->ref_count) + { + delete expression_holder_; + } + } + expression_holder_ = new expression_holder(expr); + } + } + + expression_holder* expression_holder_; + symbol_table* symbol_table_; + + friend class parser; + }; + + template + class parser + { + private: + + enum precedence_level + { + e_level00, + e_level01, + e_level02, + e_level03, + e_level04, + e_level05, + e_level06, + e_level07, + e_level08, + e_level09, + e_level10, + e_level11, + e_level12, + e_level13 + }; + + typedef ifunction F; + typedef details::expression_node expression_node_t; + typedef details::literal_node literal_node_t; + typedef details::unary_node unary_node_t; + typedef details::binary_node binary_node_t; + typedef details::trinary_node trinary_node_t; + typedef details::quaternary_node quaternary_node_t; + typedef details::quinary_node quinary_node_t; + typedef details::senary_node senary_node_t; + typedef details::conditional_node conditional_node_t; + typedef details::while_loop_node while_loop_node_t; + typedef details::variable_node variable_node_t; + typedef details::sf3_node sf3_node_t; + typedef details::sf4_node sf4_node_t; + typedef details::assignment_node assignment_node_t; + typedef details::function_node function_node_t; + typedef details::token token_t; + typedef expression_node_t* expression_node_ptr; + + public: + + parser() + : symbol_table_(0) + {} + + inline bool compile(const std::string& expression_string, expression& expr) + { + if (!validate_expression(expression_string)) + { + return false; + } + set_error(""); + expression_generator_.set_allocator(node_allocator_); + if (!lexer_.process(expression_string)) + { + set_error(lexer_.error()); + return false; + } + symbol_table_ = expr.get_symbol_table(); + next_token(); + expression_node_ptr e = parse_expression(); + if ((0 != e) && (current_token_.type == token_t::eof)) + { + expr.set_expression(e); + return !(!expr); + } + else + { + set_error("parser::compile() - Incomplete expression!"); + if (0 != e) delete e; + return false; + } + } + + inline std::string error() const + { + return error_description_; + } + + private: + + inline void store_token() + { + lexer_.store(); + store_current_token_ = current_token_; + } + + inline void restore_token() + { + lexer_.restore(); + current_token_ = store_current_token_; + } + + inline void next_token() + { + current_token_ = lexer_.next_token(); + } + + static const precedence_level default_precedence = e_level00; + + struct state_t + { + inline void set(const precedence_level& l, + const precedence_level& r, + const details::operator_type& o) + { + left = l; + right = r; + operation = o; + } + + inline void reset() + { + left = e_level00; + right = e_level00; + } + + precedence_level left; + precedence_level right; + details::operator_type operation; + }; + + inline expression_node_ptr parse_expression(precedence_level precedence = e_level00) + { + expression_node_ptr expr = parse_branch(); + + if (0 == expr) + { + return expr; + } + + bool break_loop = false; + + state_t current_state; + + for ( ; ; ) + { + current_state.reset(); + switch (current_token_.type) + { + case token_t::assign : current_state.set(e_level00,e_level00,details::e_assign); break; + case token_t::lt : current_state.set(e_level05,e_level06,details:: e_lt); break; + case token_t::lte : current_state.set(e_level05,e_level06,details:: e_lte); break; + case token_t::eq : current_state.set(e_level05,e_level06,details:: e_eq); break; + case token_t::ne : current_state.set(e_level05,e_level06,details:: e_ne); break; + case token_t::gte : current_state.set(e_level05,e_level06,details:: e_gte); break; + case token_t::gt : current_state.set(e_level05,e_level06,details:: e_gt); break; + case token_t::add : current_state.set(e_level07,e_level08,details:: e_add); break; + case token_t::sub : current_state.set(e_level07,e_level08,details:: e_sub); break; + case token_t::div : current_state.set(e_level10,e_level11,details:: e_div); break; + case token_t::mul : current_state.set(e_level10,e_level11,details:: e_mul); break; + case token_t::mod : current_state.set(e_level10,e_level11,details:: e_mod); break; + case token_t::pow : current_state.set(e_level12,e_level12,details:: e_pow); break; + default : if (current_token_.type == token_t::symbol) + { + static const std::string s_and = "and"; + static const std::string s_nand = "nand"; + static const std::string s_or = "or"; + static const std::string s_nor = "nor"; + static const std::string s_xor = "xor"; + if (details::imatch(current_token_.value,s_and)) + { + current_state.set(e_level01,e_level02,details::e_and); + break; + } + else if (details::imatch(current_token_.value,s_nand)) + { + current_state.set(e_level01,e_level02,details::e_nand); + break; + } + else if (details::imatch(current_token_.value,s_or)) + { + current_state.set(e_level03,e_level04,details::e_or); + break; + } + else if (details::imatch(current_token_.value,s_nor)) + { + current_state.set(e_level03,e_level04,details::e_nor); + break; + } + else if (details::imatch(current_token_.value,s_xor)) + { + current_state.set(e_level03,e_level04,details::e_xor); + break; + } + } + break_loop = true; + } + if (break_loop) + break; + else if (current_state.left < precedence) + break; + next_token(); + expr = expression_generator_(current_state.operation,expr,parse_expression(current_state.right)); + } + return expr; + } + + template + struct scoped_delete + { + typedef Type* ptr_t; + + scoped_delete(parser& pr, ptr_t& p) + : delete_ptr(true), + parser_(pr), + p_(&p) + {} + + scoped_delete(parser& pr, ptr_t (&p)[N]) + : delete_ptr(true), + parser_(pr), + p_(&p[0]) + {} + + ~scoped_delete() + { + if (delete_ptr) + { + for (std::size_t i = 0; i < N; ++i) + { + if (p_[i] && details::expression_node::e_variable != p_[i]->type()) + { + parser_.node_allocator_.free(p_[i]); + } + } + } + } + bool delete_ptr; + parser& parser_; + ptr_t* p_; + + private: + scoped_delete& operator=(const scoped_delete&); + }; + + template + inline expression_node_ptr parse_function_call(const details::operator_type& opt_type) + { + expression_node_ptr branch[NumberofParameters]; + expression_node_ptr result = 0; + std::fill_n(branch,NumberofParameters,reinterpret_cast(0)); + scoped_delete sd(*this,branch); + store_token(); + next_token(); + if (!token_is(token_t::lbracket)) + { + return reinterpret_cast(0); + } + for (int i = 0; i < static_cast(NumberofParameters); ++i) + { + branch[i] = parse_expression(); + if (i < static_cast(NumberofParameters - 1)) + { + if (!token_is(token_t::comma)) + { + return reinterpret_cast(0); + } + } + } + if (!token_is(token_t::rbracket)) + { + return reinterpret_cast(0); + } + else + result = expression_generator_(opt_type,branch); + sd.delete_ptr = false; + return result; + } + + template + inline expression_node_ptr parse_function_call(ifunction* function) + { + expression_node_ptr branch[NumberofParameters]; + expression_node_ptr result = 0; + std::fill_n(branch,NumberofParameters,reinterpret_cast(0)); + scoped_delete sd(*this,branch); + next_token(); + if (!token_is(token_t::lbracket)) + { + return reinterpret_cast(0); + } + for (int i = 0; i < static_cast(NumberofParameters); ++i) + { + branch[i] = parse_expression(); + if (i < static_cast(NumberofParameters - 1)) + { + if (!token_is(token_t::comma)) + { + return reinterpret_cast(0); + } + } + } + if (!token_is(token_t::rbracket)) + { + return reinterpret_cast(0); + } + else + result = expression_generator_.function(function,branch); + sd.delete_ptr = false; + return result; + } + + inline expression_node_ptr parse_conditional_statement() + { + expression_node_ptr condition = 0; + expression_node_ptr consequent = 0; + expression_node_ptr alternative = 0; + next_token(); + if (token_is(token_t::lbracket)) + condition = parse_expression(); + else + return reinterpret_cast(0); + if (token_is(token_t::comma)) + consequent = parse_expression(); + else + return reinterpret_cast(0); + if (token_is(token_t::comma)) + alternative = parse_expression(); + else + return reinterpret_cast(0); + if (token_is(token_t::rbracket)) + return expression_generator_.conditional(condition,consequent,alternative); + else + return reinterpret_cast(0); + } + + inline expression_node_ptr parse_while_loop() + { + expression_node_ptr condition = 0; + expression_node_ptr branch = 0; + next_token(); + if (token_is(token_t::lbracket)) + condition = parse_expression(); + else + return reinterpret_cast(0); + if (!token_is(token_t::rbracket)) + return reinterpret_cast(0); + if (token_is(token_t::lcrlbracket)) + branch = parse_expression(); + else + return reinterpret_cast(0); + if (token_is(token_t::rcrlbracket)) + return expression_generator_.while_loop(condition,branch); + else + return reinterpret_cast(0); + } + + inline expression_node_ptr parse_special_function(const unsigned int id) + { + //Expect: $fDD(expr0,expr1,expr2) or $fDD(expr0,expr1,expr2,expr3) + const details::operator_type opt_type = details::operator_type(id + 1000); + const std::size_t NumberOfParameters = (id < (details::e_sf18 - 1000)) ? 3 : 4; + expression_node_ptr branch3[3]; + expression_node_ptr branch4[4]; + expression_node_ptr* branch = (id < (details::e_sf18 - 1000)) ? &branch3[0] : &branch4[0]; + expression_node_ptr result = 0; + std::fill_n(branch3,3,reinterpret_cast(0)); + std::fill_n(branch4,4,reinterpret_cast(0)); + scoped_delete sd3(*this,branch3); + scoped_delete sd4(*this,branch4); + next_token(); + if (!token_is(token_t::lbracket)) + { + return reinterpret_cast(0); + } + for (std::size_t i = 0; i < NumberOfParameters; ++i) + { + branch[i] = parse_expression(); + if (i < (NumberOfParameters - 1)) + { + if (!token_is(token_t::comma)) + { + return reinterpret_cast(0); + } + } + } + if (!token_is(token_t::rbracket)) + return reinterpret_cast(0); + else + { + switch (NumberOfParameters) + { + case 3 : result = expression_generator_.special_function(opt_type,branch3); break; + case 4 : result = expression_generator_.special_function(opt_type,branch4); break; + default : return reinterpret_cast(0); + } + } + sd3.delete_ptr = false; + sd4.delete_ptr = false; + return result; + } + + inline expression_node_ptr parse_symbol() + { + std::pair match_found_error(false,""); + if (current_token_.value.size() > 1) + { + for (std::size_t i = 0; i < details::operation_list_size; ++i) + { + if (details::imatch(details::operation_list[i].name,current_token_.value)) + { + store_token(); + std::string token_value = current_token_.value; + expression_node_ptr branch = reinterpret_cast(0); + switch (details::operation_list[i].num_params) + { + case 1 : branch = parse_function_call<1>(details::operation_list[i].type); break; + case 2 : branch = parse_function_call<2>(details::operation_list[i].type); break; + case 3 : branch = parse_function_call<3>(details::operation_list[i].type); break; + case 4 : branch = parse_function_call<4>(details::operation_list[i].type); break; + case 5 : branch = parse_function_call<5>(details::operation_list[i].type); break; + case 6 : branch = parse_function_call<6>(details::operation_list[i].type); break; + } + if (branch) + { + return branch; + } + else if (!match_found_error.first) + { + match_found_error.first = true; + match_found_error.second = token_value; + set_error(""); + } + restore_token(); + } + } + + if (match_found_error.first) + { + set_error("parser::parse_branch() - invalid argument count for function: " + match_found_error.second); + return reinterpret_cast(0); + } + } + + static const std::string s_if = "if"; + static const std::string s_while = "while"; + if (details::imatch(current_token_.value,s_if)) + { + return parse_conditional_statement(); + } + else if (details::imatch(current_token_.value,s_while)) + { + return parse_while_loop(); + } + else if ((current_token_.value.size() == 4) && '$' == current_token_.value[0] && 'f' == current_token_.value[1]) + { + unsigned int id = (current_token_.value[2] - '0') * 10 + (current_token_.value[3] - '0'); + if (id <= 99) + return parse_special_function(id); + else + { + set_error("parser::parse_branch() - invalid special function: " + current_token_.value); + return reinterpret_cast(0); + } + } + else if (symbol_table_) + { + const std::string symbol = current_token_.value; + expression_node_ptr variable = symbol_table_->get_variable(symbol); + if (variable) + { + if (symbol_table_->is_constant(symbol)) + { + variable = expression_generator_(variable->value()); + } + next_token(); + return variable; + } + ifunction* function = symbol_table_->get_function(symbol); + if (function) + { + expression_node_ptr func_node = reinterpret_cast(0); + switch (function->param_count) + { + case 0 : func_node = expression_generator_.function(function); break; + case 1 : func_node = parse_function_call<1>(function); break; + case 2 : func_node = parse_function_call<2>(function); break; + case 3 : func_node = parse_function_call<3>(function); break; + case 4 : func_node = parse_function_call<4>(function); break; + case 5 : func_node = parse_function_call<5>(function); break; + case 6 : func_node = parse_function_call<6>(function); break; + case 7 : func_node = parse_function_call<7>(function); break; + case 8 : func_node = parse_function_call<8>(function); break; + case 9 : func_node = parse_function_call<9>(function); break; + default : { + set_error("parser::parse_branch() - invalid number of parameters for function: " + symbol); + return expression_node_ptr(0); + } + } + if (func_node) + { + return func_node; + } + } + set_error("parser::parse_branch() - undefined variable or function: " + symbol); + return expression_node_ptr(0); + } + else + { + set_error("parser::parse_branch() - variable or function detected, yet symbol-table is invalid" + current_token_.value); + return expression_node_ptr(0); + } + } + + inline expression_node_ptr parse_branch() + { + switch (current_token_.type) + { + case token_t::number : + { + expression_node_ptr literal_exp = expression_generator_(current_token_.numeric_value); + next_token(); + return literal_exp; + } + + case token_t::symbol : return parse_symbol(); + + case '(' : + { + next_token(); + expression_node_ptr branch = parse_expression(); + if (token_is(token_t::rbracket)) + return branch; + else + return reinterpret_cast(0); + } + case '[' : + { + next_token(); + expression_node_ptr branch = parse_expression(); + if (token_is(token_t::rsqrbracket)) + return branch; + else + return reinterpret_cast(0); + } + case '{' : + { + next_token(); + expression_node_ptr branch = parse_expression(); + if (token_is(token_t::rcrlbracket)) + return branch; + else + return reinterpret_cast(0); + } + case '-' : + { + next_token(); + return expression_generator_(details::e_neg,parse_expression(e_level09)); + } + case '+' : + { + next_token(); + return expression_generator_(details::e_pos,parse_expression(e_level09)); + } + case token_t::eof : + { + set_error("parser::parse_branch() - expected a valid branch [1]"); + return reinterpret_cast(0); + } + default : + { + set_error("parser::parse_branch() - expected a valid branch [2]"); + return reinterpret_cast(0); + } + } + } + + inline bool token_is(const typename token_t::token_type& ttype) + { + if (current_token_.type != ttype) + { + if (!((current_token_.type == ']') && (token_t::rbracket == ttype))) + { + set_error(std::string("parser::token_is() - expected: ") + static_cast(ttype)); + return false; + } + } + + next_token(); + return true; + } + + template + class expression_generator + { + public: + + typedef details::expression_node* expression_node_ptr; + + inline void set_allocator(details::node_allocator& na) + { + node_allocator_ = &na; + } + + inline expression_node_ptr operator()(const Type& v) const + { + return node_allocator_->allocate(v); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[1]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + { + if (details::e_assign == operation) + { + if (details::expression_node::e_variable == branch[0]->type()) + return synthesize_expression(operation,branch); + else + return reinterpret_cast(0); + } + else + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[5]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr (&branch)[6]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr b0) + { + expression_node_ptr branch[1] = { b0 }; + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr operator()(const details::operator_type& operation, expression_node_ptr b0, expression_node_ptr b1) + { + expression_node_ptr branch[2] = { b0, b1 }; + if (details::e_assign == operation) + { + if (details::expression_node::e_variable == branch[0]->type()) + return synthesize_expression(operation,branch); + else + return reinterpret_cast(0); + } + else + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr conditional(expression_node_ptr condition, + expression_node_ptr consequent, + expression_node_ptr alternative) const + { + //Can the condition be immediately evaluated? if so optimise. + if (expression_node_t::e_constant == condition->type()) + { + if (condition->value() != Type(0)) + { + node_allocator_->free(condition); + node_allocator_->free(alternative); + return consequent; + } + else + { + node_allocator_->free(condition); + node_allocator_->free(consequent); + return alternative; + } + } + else + return node_allocator_->allocate(condition,consequent,alternative); + } + + inline expression_node_ptr while_loop(expression_node_ptr condition, + expression_node_ptr branch) const + { + return node_allocator_->allocate(condition,branch); + } + + inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[3]) + { + return synthesize_expression(operation,branch); + } + + inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[4]) + { + return synthesize_expression(operation,branch); + } + + template + inline expression_node_ptr function(typename function_node_t::ifunction* f, expression_node_ptr (&b)[N]) + { + expression_node_ptr result = synthesize_expression(f,b); + if (result) + { + function_node_t* func_node_ptr = dynamic_cast(result); + if (func_node_ptr) + { + func_node_ptr->init_branches(b); + } + else + { + node_allocator_->free(result); + result = reinterpret_cast(0); + } + } + return result; + } + + inline expression_node_ptr function(typename function_node_t::ifunction* f) + { + return node_allocator_->allocate(f); + } + + private: + + template + inline bool is_contant_node(NodePtr n) const + { + return (expression_node_t::e_constant == n->type()); + } + + template + inline bool is_constant_foldable(NodePtr (&b)[N]) const + { + for (std::size_t i = 0; i < N; ++i) + { + if (b[i] && !is_contant_node(b[i])) return false; + } + return true; + } + + template + inline bool all_nodes_valid(expression_node_ptr (&b)[N]) const + { + for (std::size_t i = 0; i < N; ++i) + { + if (0 == b[i]) return false; + } + return true; + } + + template + inline expression_node_ptr synthesize_expression(const details::operator_type& operation, expression_node_ptr (&branch)[N]) + { + if ((details::e_default != operation) && all_nodes_valid(branch)) + { + //Attempt simple constant folding optimisation. + expression_node_ptr expression_point = node_allocator_->allocate(operation,branch); + if (is_constant_foldable(branch)) + { + Type v = expression_point->value(); + node_allocator_->free(expression_point); + return node_allocator_->allocate(v); + } + else + return expression_point; + } + else + return reinterpret_cast(0); + } + + template + inline expression_node_ptr synthesize_expression(F* f, expression_node_ptr (&branch)[N]) + { + if (all_nodes_valid(branch)) + { + //Attempt simple constant folding optimisation. + expression_node_ptr expression_point = node_allocator_->allocate(f); + dynamic_cast(expression_point)->init_branches(branch); + if (is_constant_foldable(branch)) + { + Type v = expression_point->value(); + node_allocator_->free(expression_point); + return node_allocator_->allocate(v); + } + else + return expression_point; + } + else + return reinterpret_cast(0); + } + + details::node_allocator* node_allocator_; + }; + + inline bool check0(const char c0, const char c1, + const char v0, const char v1, + const char v2) + { + return ((c0 == v0) && ((c1 == v1) || (c1 == v2))); + } + + inline bool check1(const char c0, const char c1, + const char v0, const char v1) + { + return ((c0 == v0) && (c1 == v1)); + } + + inline bool validate_expression(const std::string& expression_string) + { + if (expression_string.empty()) + { + set_error("parser::validate_expression() - empty expression"); + return false; + } + + std::stack bracket_stack; + + for (std::size_t i = 0; i < (expression_string.size() - 1); ++i) + { + char c0 = expression_string[i]; + char c1 = expression_string[i + 1]; + if (details::is_invalid(c0)) + { + set_error(std::string("parser::validate_expression() - invalid character: ") + c0); + return false; + } + else if ( + check0(c0,c1,'*','*','/') || + check0(c0,c1,'*','%','^') || + check0(c0,c1,'/','*','/') || + check0(c0,c1,'/','%','^') || + check0(c0,c1,'+','*','/') || + check0(c0,c1,'+','%','^') || + check0(c0,c1,'-','*','/') || + check0(c0,c1,'-','%','^') || + check0(c0,c1,'^','*','/') || + check0(c0,c1,'^','^','%') || + check0(c0,c1,'%','*','/') || + check0(c0,c1,'%','^','%') || + check0(c0,c1,'.','%','^') || + check0(c0,c1,'.','*','/') || + check0(c0,c1,',','%','^') || + check0(c0,c1,',','*','/') || + check0(c0,c1,'(','*','/') || + check0(c0,c1,'(','%','^') || + check0(c0,c1,'[','*','/') || + check0(c0,c1,'[','%','^') || + check0(c0,c1,'{','*','/') || + check0(c0,c1,'{','%','^') || + check0(c0,c1,'+',')',']') || + check0(c0,c1,'-',')',']') || + check0(c0,c1,'*',')',']') || + check0(c0,c1,'/',')',']') || + check0(c0,c1,'^',')',']') || + check0(c0,c1,'%',')',']') || + check1(c0,c1,'+','}' ) || + check1(c0,c1,'-','}' ) || + check1(c0,c1,'*','}' ) || + check1(c0,c1,'/','}' ) || + check1(c0,c1,'^','}' ) || + check1(c0,c1,'%','}' ) || + check1(c0,c1,'.','.' ) || + check1(c0,c1,'.','+' ) || + check1(c0,c1,'.','-' ) || + check1(c0,c1,'.','*' ) || + check1(c0,c1,'.','/' ) || + check1(c0,c1,',',',' ) + ) + { + set_error(std::string("parser::validate_expression() - invalid character combination: ") + expression_string.substr(i,2)); + return false; + } + else if (c0 == '(') + bracket_stack.push(')'); + else if (c0 == '[') + bracket_stack.push(']'); + else if (c0 == '{') + bracket_stack.push('}'); + else if (details::is_right_bracket(c0)) + { + if (bracket_stack.empty()) + { + set_error(std::string("parser::validate_expression() - invalid/mismatched bracket(s)[0]: ") + expression_string.substr(0,i)); + return false; + } + else if (c0 != bracket_stack.top()) + { + set_error(std::string("parser::validate_expression() - invalid/mismatched bracket(s)[1]: ") + expression_string.substr(0,i)); + return false; + } + else + bracket_stack.pop(); + } + } + + if (!bracket_stack.empty()) + { + if (1 == bracket_stack.size()) + { + char c0 = expression_string[expression_string.size() - 1]; + if (details::is_right_bracket(c0)) + { + if (c0 == bracket_stack.top()) + return true; + else + { + set_error(std::string("parser::validate_expression() - invalid/mismatched bracket(s)[2]: ") + expression_string); + return false; + } + } + } + set_error(std::string("parser::validate_expression() - invalid/mismatched bracket(s)[3]: ") + expression_string); + return false; + } + return true; + } + + inline void set_error(const std::string& err_str) + { + //would it be better if this were a stack? + if (error_description_.empty()) + { + error_description_ = err_str; + } + } + + private: + + details::lexer lexer_; + details::token current_token_; + details::token store_current_token_; + expression_generator expression_generator_; + details::node_allocator node_allocator_; + symbol_table* symbol_table_; + std::string error_description_; + }; + + template + inline T integrate(expression& e, + T& x, + const T& r0, const T& r1, + const std::size_t number_of_intervals = 1000000) + { + if (r0 > r1) return T(0); + T h = (r1 - r0) / (T(2.0) * number_of_intervals); + T total_area = T(0); + for (std::size_t i = 0; i < number_of_intervals; ++i) + { + x = r0 + T(2.0) * i * h; + T y0 = e.value(); x += h; + T y1 = e.value(); x += h; + T y2 = e.value(); x += h; + total_area += h * (y0 + T(4.0) * y1 + y2) / T(3.0); + } + return total_area; + } + + template + inline T integrate(expression& e, + const std::string& variable_name, + const T& r0, const T& r1, + const std::size_t number_of_intervals = 1000000) + { + details::variable_node* var = e.get_symbol_table()->get_variable(variable_name); + if (var) + { + T& x = var->ref(); + return integrate(e,x,r0,r1,number_of_intervals); + } + else + return std::numeric_limits::quiet_NaN(); + } + + template + inline T derivative(expression& e, + T& x, + const double& h = 0.00001) + { + 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(8.0) * (y1 - y2) + y3) / (T(12.0) * h); + } + + template + inline T derivative(expression& e, + const std::string& variable_name, + const double& h = 0.00001) + { + details::variable_node* var = e.get_symbol_table()->get_variable(variable_name); + if (var) + { + T& x = var->ref(); + return derivative(e,x,h); + } + else + return std::numeric_limits::quiet_NaN(); + } + + namespace information + { + static const char* library = "Mathematical Expression Toolkit"; + static const char* version = "2.718281828"; + static const char* date = "20111111"; + + static inline std::string data() + { + static const std::string info_str = std::string(library) + + std::string(" v") + std::string(version) + + std::string(" (") + date + std::string(")"); + return info_str; + } + + } // namespace information + +} // namespace exprtk + +#endif diff --git a/exprtk_test.cpp b/exprtk_test.cpp new file mode 100644 index 0000000..a933c20 --- /dev/null +++ b/exprtk_test.cpp @@ -0,0 +1,1173 @@ +/* + ************************************************************** + * C++ Mathematical Expression Toolkit Library * + * * + * Examples and Unit-Tests * + * Author: Arash Partow (1999-2012) * + * URL: http://www.partow.net/programming/exprtk/index.html * + * * + * Copyright notice: * + * Free use of the 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 * + * * + ************************************************************** +*/ + + +#include +#include +#include +#include + +#include "exprtk.hpp" + +typedef std::pair test_t; + +static const test_t test_list[] = + { + test_t("0",0.0), + test_t("1",1.0), + test_t("2",2.0), + test_t("3",3.0), + test_t("4",4.0), + test_t("5",5.0), + test_t("6",6.0), + test_t("7",7.0), + test_t("8",8.0), + test_t("9",9.0), + test_t("0.0",0.0), + test_t("1.0",1.0), + test_t("2.0",2.0), + test_t("3.0",3.0), + test_t("4.0",4.0), + test_t("5.0",5.0), + test_t("6.0",6.0), + test_t("7.0",7.0), + test_t("8.0",8.0), + test_t("9.0",9.0), + test_t("0.0",0.0), + test_t("1.1",1.1), + test_t("2.2",2.2), + test_t("3.3",3.3), + test_t("4.4",4.4), + test_t("5.5",5.5), + test_t("6.6",6.6), + test_t("7.7",7.7), + test_t("8.8",8.8), + test_t("9.9",9.9), + test_t("+0",0.0), + test_t("+1",1.0), + test_t("+2",2.0), + test_t("+3",3.0), + test_t("+4",4.0), + test_t("+5",5.0), + test_t("+6",6.0), + test_t("+7",7.0), + test_t("+8",8.0), + test_t("+9",9.0), + test_t("+0.0",0.0), + test_t("+1.0",1.0), + test_t("+2.0",2.0), + test_t("+3.0",3.0), + test_t("+4.0",4.0), + test_t("+5.0",5.0), + test_t("+6.0",6.0), + test_t("+7.0",7.0), + test_t("+8.0",8.0), + test_t("+9.0",9.0), + test_t("+0.0",0.0), + test_t("+1.1",1.1), + test_t("+2.2",2.2), + test_t("+3.3",3.3), + test_t("+4.4",4.4), + test_t("+5.5",5.5), + test_t("+6.6",6.6), + test_t("+7.7",7.7), + test_t("+8.8",8.8), + test_t("+9.9",9.9), + test_t("-0",-0.0), + test_t("-1",-1.0), + test_t("-2",-2.0), + test_t("-3",-3.0), + test_t("-4",-4.0), + test_t("-5",-5.0), + test_t("-6",-6.0), + test_t("-7",-7.0), + test_t("-8",-8.0), + test_t("-9",-9.0), + test_t("-0.0",-0.0), + test_t("-1.0",-1.0), + test_t("-2.0",-2.0), + test_t("-3.0",-3.0), + test_t("-4.0",-4.0), + test_t("-5.0",-5.0), + test_t("-6.0",-6.0), + test_t("-7.0",-7.0), + test_t("-8.0",-8.0), + test_t("-9.0",-9.0), + test_t("-0.0",-0.0), + test_t("-1.1",-1.1), + test_t("-2.2",-2.2), + test_t("-3.3",-3.3), + test_t("-4.4",-4.4), + test_t("-5.5",-5.5), + test_t("-6.6",-6.6), + test_t("-7.7",-7.7), + test_t("-8.8",-8.8), + test_t("-9.9",-9.9), + test_t("0.0e+0",+0.0e+0), + test_t("1.1e+1",+1.1e+1), + test_t("2.2e+2",+2.2e+2), + test_t("3.3e+3",+3.3e+3), + test_t("4.4e+4",+4.4e+4), + test_t("5.5e+5",+5.5e+5), + test_t("6.6e+6",+6.6e+6), + test_t("7.7e+7",+7.7e+7), + test_t("8.8e+8",+8.8e+8), + test_t("9.9e+9",+9.9e+9), + test_t("-0.0e+0",-0.0e+0), + test_t("-1.1e+1",-1.1e+1), + test_t("-2.2e+2",-2.2e+2), + test_t("-3.3e+3",-3.3e+3), + test_t("-4.4e+4",-4.4e+4), + test_t("-5.5e+5",-5.5e+5), + test_t("-6.6e+6",-6.6e+6), + test_t("-7.7e+7",-7.7e+7), + test_t("-8.8e+8",-8.8e+8), + test_t("-9.9e+9",-9.9e+9), + test_t("0.0E+0",+0.0E+0), + test_t("1.1E+1",+1.1E+1), + test_t("2.2E+2",+2.2E+2), + test_t("3.3E+3",+3.3E+3), + test_t("4.4E+4",+4.4E+4), + test_t("5.5E+5",+5.5E+5), + test_t("6.6E+6",+6.6E+6), + test_t("7.7E+7",+7.7E+7), + test_t("8.8E+8",+8.8E+8), + test_t("9.9E+9",+9.9E+9), + test_t("-0.0E+0",-0.0E+0), + test_t("-1.1E+1",-1.1E+1), + test_t("-2.2E+2",-2.2E+2), + test_t("-3.3E+3",-3.3E+3), + test_t("-4.4E+4",-4.4E+4), + test_t("-5.5E+5",-5.5E+5), + test_t("-6.6E+6",-6.6E+6), + test_t("-7.7E+7",-7.7E+7), + test_t("-8.8E+8",-8.8E+8), + test_t("-9.9E+9",-9.9E+9), + test_t("(0)",0.0), + test_t("(1)",1.0), + test_t("(2)",2.0), + test_t("(3)",3.0), + test_t("(4)",4.0), + test_t("(5)",5.0), + test_t("(6)",6.0), + test_t("(7)",7.0), + test_t("(8)",8.0), + test_t("(9)",9.0), + test_t("(0.0)",0.0), + test_t("(1.0)",1.0), + test_t("(2.0)",2.0), + test_t("(3.0)",3.0), + test_t("(4.0)",4.0), + test_t("(5.0)",5.0), + test_t("(6.0)",6.0), + test_t("(7.0)",7.0), + test_t("(8.0)",8.0), + test_t("(9.0)",9.0), + test_t("(0.0)",0.0), + test_t("(1.1)",1.1), + test_t("(2.2)",2.2), + test_t("(3.3)",3.3), + test_t("(4.4)",4.4), + test_t("(5.5)",5.5), + test_t("(6.6)",6.6), + test_t("(7.7)",7.7), + test_t("(8.8)",8.8), + test_t("(9.9)",9.9), + test_t("(+0)",0.0), + test_t("(+1)",1.0), + test_t("(+2)",2.0), + test_t("(+3)",3.0), + test_t("(+4)",4.0), + test_t("(+5)",5.0), + test_t("(+6)",6.0), + test_t("(+7)",7.0), + test_t("(+8)",8.0), + test_t("(+9)",9.0), + test_t("(+0.0)",0.0), + test_t("(+1.0)",1.0), + test_t("(+2.0)",2.0), + test_t("(+3.0)",3.0), + test_t("(+4.0)",4.0), + test_t("(+5.0)",5.0), + test_t("(+6.0)",6.0), + test_t("(+7.0)",7.0), + test_t("(+8.0)",8.0), + test_t("(+9.0)",9.0), + test_t("(+0.0)",0.0), + test_t("(+1.1)",1.1), + test_t("(+2.2)",2.2), + test_t("(+3.3)",3.3), + test_t("(+4.4)",4.4), + test_t("(+5.5)",5.5), + test_t("(+6.6)",6.6), + test_t("(+7.7)",7.7), + test_t("(+8.8)",8.8), + test_t("(+9.9)",9.9), + test_t("(-0)",-0.0), + test_t("(-1)",-1.0), + test_t("(-2)",-2.0), + test_t("(-3)",-3.0), + test_t("(-4)",-4.0), + test_t("(-5)",-5.0), + test_t("(-6)",-6.0), + test_t("(-7)",-7.0), + test_t("(-8)",-8.0), + test_t("(-9)",-9.0), + test_t("(-0.0)",-0.0), + test_t("(-1.0)",-1.0), + test_t("(-2.0)",-2.0), + test_t("(-3.0)",-3.0), + test_t("(-4.0)",-4.0), + test_t("(-5.0)",-5.0), + test_t("(-6.0)",-6.0), + test_t("(-7.0)",-7.0), + test_t("(-8.0)",-8.0), + test_t("(-9.0)",-9.0), + test_t("(-0.0)",-0.0), + test_t("(-1.1)",-1.1), + test_t("(-2.2)",-2.2), + test_t("(-3.3)",-3.3), + test_t("(-4.4)",-4.4), + test_t("(-5.5)",-5.5), + test_t("(-6.6)",-6.6), + test_t("(-7.7)",-7.7), + test_t("(-8.8)",-8.8), + test_t("(-9.9)",-9.9), + test_t("1234567890",1234567890), + test_t("123456789.0",123456789.0), + test_t("+1234567890",1234567890), + test_t("+123456789.0",123456789.0), + test_t("-1234567890",-1234567890), + test_t("-123456789.0",-123456789.0), + test_t("1234.567890",1234.567890), + test_t("-1234.567890",-1234.567890), + test_t("0+9",9.0), + test_t("1+8",9.0), + test_t("2+7",9.0), + test_t("3+6",9.0), + test_t("4+5",9.0), + test_t("5+4",9.0), + test_t("6+3",9.0), + test_t("7+2",9.0), + test_t("8+1",9.0), + test_t("9+0",9.0), + test_t(" 0 + 9 ",9.0), + test_t(" 1 + 8 ",9.0), + test_t(" 2 + 7 ",9.0), + test_t(" 3 + 6 ",9.0), + test_t(" 4 + 5 ",9.0), + test_t(" 5 + 4 ",9.0), + test_t(" 6 + 3 ",9.0), + test_t(" 7 + 2 ",9.0), + test_t(" 8 + 1 ",9.0), + test_t(" 9 + 0 ",9.0), + test_t("( 0 + 9 )",9.0), + test_t("( 1 + 8 )",9.0), + test_t("( 2 + 7 )",9.0), + test_t("( 3 + 6 )",9.0), + test_t("( 4 + 5 )",9.0), + test_t("( 5 + 4 )",9.0), + test_t("( 6 + 3 )",9.0), + test_t("( 7 + 2 )",9.0), + test_t("( 8 + 1 )",9.0), + test_t("( 9 + 0 )",9.0), + test_t("1+2",+3.0), + test_t("1-2",-1.0), + test_t("1*2",+2.0), + test_t("1/2",+0.5), + test_t("1.1+2.2", +3.3), + test_t("1.1-2.2", -1.1), + test_t("1.1*2.2",+2.42), + test_t("1.1/2.2", +0.5), + test_t("0-9",-9.0), + test_t("1-8",-7.0), + test_t("2-7",-5.0), + test_t("3-6",-3.0), + test_t("4-5",-1.0), + test_t("5-4",+1.0), + test_t("6-3",+3.0), + test_t("7-2",+5.0), + test_t("8-1",+7.0), + test_t("9-0",+9.0), + test_t(" 0 - 9 ",-9.0), + test_t(" 1 - 8 ",-7.0), + test_t(" 2 - 7 ",-5.0), + test_t(" 3 - 6 ",-3.0), + test_t(" 4 - 5 ",-1.0), + test_t(" 5 - 4 ",+1.0), + test_t(" 6 - 3 ",+3.0), + test_t(" 7 - 2 ",+5.0), + test_t(" 8 - 1 ",+7.0), + test_t(" 9 - 0 ",+9.0), + test_t("( 0 - 9 )",-9.0), + test_t("( 1 - 8 )",-7.0), + test_t("( 2 - 7 )",-5.0), + test_t("( 3 - 6 )",-3.0), + test_t("( 4 - 5 )",-1.0), + test_t("( 5 - 4 )",+1.0), + test_t("( 6 - 3 )",+3.0), + test_t("( 7 - 2 )",+5.0), + test_t("( 8 - 1 )",+7.0), + test_t("( 9 - 0 )",+9.0), + test_t("1.1+2.2+3.3",+6.6), + test_t("+1.1+2.2+3.3",+6.6), + test_t("-1.1-2.2-3.3",-6.6), + test_t("1.1*2.2*3.3",+7.986), + test_t("+1.1*2.2*3.3",+7.986), + test_t("-1.1*-2.2*-3.3",-7.986), + test_t("1 + 1/2",+1.5), + test_t("1 + (1/2)",+1.5), + test_t("1.1 + 1.1/2.2",+1.6), + test_t("1.1 + (1.1/2.2)",+1.6), + test_t("2 * 1/2",+1.0), + test_t("2 * (1/2)",+1.0), + test_t("2.2 * 1.1/2.2",+1.1), + test_t("2.2 * (1.1/2.2)",+1.1), + test_t("1^2",1.0), + test_t("2^1",2.0), + test_t("2^3",8.0), + test_t("3^2^1",9.0), + test_t("1.1^2.2",1.23328630055466251099), + test_t("2.2^1.1",2.3804822576003541627), + test_t("2.2^3.3",13.48946876053338489127), + test_t("3.3^2.2^1.1",17.15193942371376191362), + test_t("1.1^(1.1 * 2.2)", 1.25941916576299080582), + test_t("2.2^(1.1 * 3.3)",17.49823848953534759743), + test_t("3.3^(1.1 * 2.2)",17.98058156638874965269), + test_t("equal(1.0^(1.0/2.0),sqrt(1.0))",1.0), + test_t("equal(1.0^(1.0/2.0),root(1.0,2.0))",1.0), + test_t("equal(1.0^(1.0/3.0),root(1.0,3.0))",1.0), + test_t("equal(1.0^(1.0/4.0),root(1.0,4.0))",1.0), + test_t("equal(1.0^(1.0/5.0),root(1.0,5.0))",1.0), + test_t("equal(1.0^(1.0/6.0),root(1.0,6.0))",1.0), + test_t("equal(1.0^(1.0/7.0),root(1.0,7.0))",1.0), + test_t("equal(1.0^(1.0/8.0),root(1.0,8.0))",1.0), + test_t("equal(1.0^(1.0/9.0),root(1.0,9.0))",1.0), + test_t("equal(2.0^(1.0/2.0),sqrt(2.0))",1.0), + test_t("equal(2.0^(1.0/2.0),root(2.0,2.0))",1.0), + test_t("equal(3.0^(1.0/3.0),root(3.0,3.0))",1.0), + test_t("equal(4.0^(1.0/4.0),root(4.0,4.0))",1.0), + test_t("equal(5.0^(1.0/5.0),root(5.0,5.0))",1.0), + test_t("equal(6.0^(1.0/6.0),root(6.0,6.0))",1.0), + test_t("equal(7.0^(1.0/7.0),root(7.0,7.0))",1.0), + test_t("equal(8.0^(1.0/8.0),root(8.0,8.0))",1.0), + test_t("equal(9.0^(1.0/9.0),root(9.0,9.0))",1.0), + test_t("1 < 2", 1.0), + test_t("1 <= 2", 1.0), + test_t("1.1 <= 2.2", 1.0), + test_t("(1.0 + 0.1) <= (2.0 + 0.2)", 1.0), + test_t("1 > 2", 0.0), + test_t("1 >= 2", 0.0), + test_t("1.1 >= 2.2", 0.0), + test_t("(1.0 + 0.1) >= (2.0 + 0.2)", 0.0), + test_t("1 <> 2", 1.0), + test_t("1 != 2", 1.0), + test_t("1.1 <> 2.2", 1.0), + test_t("1.1 != 2.2", 1.0), + test_t("(1.0 + 0.1) <> (2.0 + 0.2)", 1.0), + test_t("(1.0 + 0.1) != (2.0 + 0.2)", 1.0), + test_t("1 == 1", 1.0), + test_t("1.1 == 1.1", 1.0), + test_t("1 = 1", 1.0), + test_t("1.1 = 1.1", 1.0), + test_t("1 <> 1", 0.0), + test_t("1 != 1", 0.0), + test_t("1.1 <> 1.1", 0.0), + test_t("1.1 != 1.1", 0.0), + test_t("(1.0 + 0.1) <> (1.0 + 0.1)", 0.0), + test_t("(1.0 + 0.1) != (1.0 + 0.1)", 0.0), + test_t("equal(1.1,1.1)",1.0), + test_t("equal(1.1,2.2)",0.0), + test_t("not_equal(1.1,1.1)",0.0), + test_t("not_equal(1.1,2.2)",1.0), + test_t("1 and 1",1.0), + test_t("1 and 0",0.0), + test_t("0 and 1",0.0), + test_t("0 and 0",0.0), + test_t("1 nand 1",0.0), + test_t("1 nand 0",1.0), + test_t("0 nand 1",1.0), + test_t("0 nand 0",1.0), + test_t("1 or 1",1.0), + test_t("1 or 0",1.0), + test_t("0 or 1",1.0), + test_t("0 or 0",0.0), + test_t("1 nor 1",0.0), + test_t("1 nor 0",0.0), + test_t("0 nor 1",0.0), + test_t("0 nor 0",1.0), + test_t("0 xor 0",0.0), + test_t("0 xor 1",1.0), + test_t("1 xor 0",1.0), + test_t("1 xor 1",0.0), + test_t("1.0 and 1.0",1.0), + test_t("1.0 and 0.0",0.0), + test_t("0.0 and 1.0",0.0), + test_t("0.0 and 0.0",0.0), + test_t("1.0 nand 1.0",0.0), + test_t("1.0 nand 0.0",1.0), + test_t("0.0 nand 1.0",1.0), + test_t("0.0 nand 0.0",1.0), + test_t("1.0 or 1.0",1.0), + test_t("1.0 or 0.0",1.0), + test_t("0.0 or 1.0",1.0), + test_t("0.0 or 0.0",0.0), + test_t("1.0 nor 1.0",0.0), + test_t("1.0 nor 0.0",0.0), + test_t("0.0 nor 1.0",0.0), + test_t("0.0 nor 0.0",1.0), + test_t("0.0 xor 0.0",0.0), + test_t("0.0 xor 1.0",1.0), + test_t("1.0 xor 0.0",1.0), + test_t("1.0 xor 1.0",0.0), + test_t("(1 and 1)",1.0), + test_t("(1 and 0)",0.0), + test_t("(0 and 1)",0.0), + test_t("(0 and 0)",0.0), + test_t("(1 nand 1)",0.0), + test_t("(1 nand 0)",1.0), + test_t("(0 nand 1)",1.0), + test_t("(0 nand 0)",1.0), + test_t("(1 or 1)",1.0), + test_t("(1 or 0)",1.0), + test_t("(0 or 1)",1.0), + test_t("(0 or 0)",0.0), + test_t("(1 nor 1)",0.0), + test_t("(1 nor 0)",0.0), + test_t("(0 nor 1)",0.0), + test_t("(0 nor 0)",1.0), + test_t("(0 xor 0)",0.0), + test_t("(0 xor 1)",1.0), + test_t("(1 xor 0)",1.0), + test_t("(1 xor 1)",0.0), + test_t("(1.0 and 1.0)",1.0), + test_t("(1.0 and 0.0)",0.0), + test_t("(0.0 and 1.0)",0.0), + test_t("(0.0 and 0.0)",0.0), + test_t("(1.0 nand 1.0)",0.0), + test_t("(1.0 nand 0.0)",1.0), + test_t("(0.0 nand 1.0)",1.0), + test_t("(0.0 nand 0.0)",1.0), + test_t("(1.0 or 1.0)",1.0), + test_t("(1.0 or 0.0)",1.0), + test_t("(0.0 or 1.0)",1.0), + test_t("(0.0 or 0.0)",0.0), + test_t("(1.0 nor 1.0)",0.0), + test_t("(1.0 nor 0.0)",0.0), + test_t("(0.0 nor 1.0)",0.0), + test_t("(0.0 nor 0.0)",1.0), + test_t("(0.0 xor 0.0)",0.0), + test_t("(0.0 xor 1.0)",1.0), + test_t("(1.0 xor 0.0)",1.0), + test_t("(1.0 xor 1.0)",0.0), + test_t("(1 nand 1) == not(1 and 1)",1.0), + test_t("(1 nand 0) == not(1 and 0)",1.0), + test_t("(0 nand 1) == not(0 and 1)",1.0), + test_t("(0 nand 0) == not(0 and 0)",1.0), + test_t("(1 nor 1) == not(1 or 1)",1.0), + test_t("(1 nor 0) == not(1 or 0)",1.0), + test_t("(0 nor 1) == not(0 or 1)",1.0), + test_t("(0 nor 0) == not(0 or 0)",1.0), + test_t("abs(1)",1.0), + test_t("abs(-1)",1.0), + test_t("abs(1.0)",1.0), + test_t("abs(-1.0)",1.0), + test_t("min(1,2)",1.0), + test_t("min(1,2,3)",1.0), + test_t("min(1,2,3,4)",1.0), + test_t("min(1,2,3,4,5)",1.0), + test_t("min(1,2,3,4,5,6)",1.0), + test_t("min(1.1,2.2)",1.1), + test_t("min(1.1,2.2,3.3)",1.1), + test_t("min(1.1,2.2,3.3,4.4)",1.1), + test_t("min(1.1,2.2,3.3,4.4,5.5)",1.1), + test_t("min(1.1,2.2,3.3,4.4,5.5,6.6)",1.1), + test_t("min(min(1,2),min(3,4))",1.0), + test_t("max(1,2)",2.0), + test_t("max(1,2,3)",3.0), + test_t("max(1,2,3,4)",4.0), + test_t("max(1,2,3,4,5)",5.0), + test_t("max(1,2,3,4,5,6)",6.0), + test_t("max(1.1,2.2)",2.2), + test_t("max(1.1,2.2,3.3)",3.3), + test_t("max(1.1,2.2,3.3,4.4)",4.4), + test_t("max(1.1,2.2,3.3,4.4,5.5)",5.5), + test_t("max(1.1,2.2,3.3,4.4,5.5,6.6)",6.6), + test_t("max(max(1,2),max(3,4))",4.0), + test_t("avg(1,2)",1.5), + test_t("avg(1,2,3)",2.0), + test_t("avg(1,2,3,4)",2.5), + test_t("avg(1,2,3,4,5)",3.0), + test_t("avg(1.1,2.2)",1.65), + test_t("avg(1.1,2.2,3.3)",2.2), + test_t("avg(1.1,2.2,3.3,4.4)",2.75), + test_t("avg(1.1,2.2,3.3,4.4,5.5)",3.3), + test_t("avg(1.1,2.2,3.3,4.4,5.5,6.6)",3.85), + test_t("sum(1,2)",3.0), + test_t("sum(1,2,3)",6.0), + test_t("sum(1,2,3,4)",10), + test_t("sum(1,2,3,4,5)",15.0), + test_t("sum(1,2,3,4,5,6)",21), + test_t("sum(1.1,2.2)",3.3), + test_t("sum(1.1,2.2,3.3)",6.6), + test_t("sum(1.1,2.2,3.3,4.4)",11.0), + test_t("sum(1.1,2.2,3.3,4.4,5.5)",16.5), + test_t("sum(1.1,2.2,3.3,4.4,5.5,6.6)",23.1), + test_t("mul(1,2)",2.0), + test_t("mul(1,2,3)",6.0), + test_t("mul(1,2,3,4)",24.0), + test_t("mul(1,2,3,4,5)",120.0), + test_t("mul(1,2,3,4,5,6)",720.0), + test_t("mul(1.1,2.2)",2.42), + test_t("mul(1.1,2.2,3.3)",7.986), + test_t("mul(1.1,2.2,3.3,4.4)",35.1384), + test_t("mul(1.1,2.2,3.3,4.4,5.5)",193.2612), + test_t("mul(1.1,2.2,3.3,4.4,5.5,6.6)",1275.52392), + test_t("floor(1.0)",1.0), + test_t("floor(1.1)",1.0), + test_t("floor(-1.0)",-1.0), + test_t("floor(-1.1)",-2.0), + test_t("ceil(1.0)",1.0), + test_t("ceil(1.1)",2.0), + test_t("ceil(-1.0)",-1.0), + test_t("ceil(-1.1)",-1.0), + test_t("round(1.1)",1.0), + test_t("round(1.49)",1.0), + test_t("round(1.5)",2.0), + test_t("round(1.9)",2.0), + test_t("roundn(1/3,2)",0.33), + test_t("roundn(1/3,5)",0.33333), + test_t("roundn(2/3,2)",0.67), + test_t("roundn(2/3,5)",0.66667), + test_t("roundn(1.0/3.0,2.0)",0.33), + test_t("roundn(1.0/3.0,5.0)",0.33333), + test_t("roundn(2.0/3.0,2.0)",0.67), + test_t("roundn(2.0/3.0,5.0)",0.66667), + test_t("cos(0.0)",1.0), + test_t("sin(0.0)",0.0), + test_t("equal(sin(pi/4.0),cos(pi/4.0))",1.0), + test_t("equal(sin(pi/6.0),cos(pi/3.0))",1.0), + test_t("(sin(pi/4.0) - cos(pi/4.0)) <= epsilon",1.0), + test_t("(cos(pi/3.0) - sin(pi/6.0)) <= epsilon",1.0), + test_t("sin(deg2rad(30))",0.5), + test_t("cos(deg2rad(60))",0.5), + test_t("sin(deg2rad(30)) + cos(deg2rad(60))",1.0), + test_t("equal(sin(deg2rad(30))/cos(deg2rad(30)),tan(deg2rad(30)))",1.0), + test_t("exp(1.0)",2.71828182845904523536028747135266249775724), + test_t("exp(0.0)",1.0), + test_t("log(2.7182818284590451)",1.0), + test_t("log10(10.0)",1.0), + test_t("hyp(3.0,4.0)",5.0), + test_t("hyp(1.0,sqrt(3.0))",2.0), + test_t("if(1 < 2, 3, 4)",3.0), + test_t("if(1.1 < 2.2, 3.3, 4.4)",3.3), + test_t("if((1.0+1.1) < (2.0+1.2), 3.3, 4.4)",3.3), + test_t("if(1 = 2, 3, 4)",4.0), + test_t("if(1.1 = 2.2, 3.3, 4.4)",4.4), + test_t("if((1.0+1.1) = (2.0+1.2), 3.3, 4.4)",4.4), + test_t("if(1 == 2, 3, 4)",4.0), + test_t("if(1.1 == 2.2, 3.3, 4.4)",4.4), + test_t("if((1.0+1.1) == (2.0+1.2), 3.3, 4.4)",4.4), + test_t("if(1 >= 2, 3, 4)",4.0), + test_t("if(1.1 >= 2.2, 3.3, 4.4)",4.4), + test_t("if((1.0+1.1) >= (2.0+1.2), 3.3, 4.4)",4.4), + test_t("if(((1.0 + 2.0) == 3.0) and ((4.0 + 5.0) < 9.0),1,2)",2.0), + test_t("(3.0 - 1.0 - 2.0) == ((3.0 - 1.0) - 2.0)",1.0), + test_t("clamp(-1,1,+1)",1.0), + test_t("clamp(-1,-1.5,+1.0)",-1.0), + test_t("clamp(-1,+1.5,+1.0)",+1.0), + test_t("clamp(-1,-1.5,+1.0) + clamp(-1,+1.5,+1.0)",0.0), + test_t("inrange(-2,1,+2) == ((-2 <= 1) and (1 <= +2))",1.0), + test_t("inrange(-2,1,+2) == if(({-2 <= 1} and [1 <= +2]),1.0,0.0)",1.0), + test_t("equal($f00(1.1,2.2,3.3),((1.1+2.2)/3.3))",1.0), + test_t("equal($f01(1.1,2.2,3.3),((1.1+2.2)*3.3))",1.0), + test_t("equal($f02(1.1,2.2,3.3),((1.1-2.2)/3.3))",1.0), + test_t("equal($f03(1.1,2.2,3.3),((1.1-2.2)*3.3))",1.0), + test_t("equal($f04(1.1,2.2,3.3),((1.1*2.2)+3.3))",1.0), + test_t("equal($f05(1.1,2.2,3.3),((1.1*2.2)-3.3))",1.0), + test_t("equal($f06(1.1,2.2,3.3),((1.1*2.2)/3.3))",1.0), + test_t("equal($f07(1.1,2.2,3.3),((1.1*2.2)*3.3))",1.0), + test_t("equal($f08(1.1,2.2,3.3),((1.1/2.2)+3.3))",1.0), + test_t("equal($f09(1.1,2.2,3.3),((1.1/2.2)-3.3))",1.0), + test_t("equal($f10(1.1,2.2,3.3),((1.1/2.2)/3.3))",1.0), + test_t("equal($f11(1.1,2.2,3.3),((1.1/2.2)*3.3))",1.0), + test_t("equal($f12(1.1,2.2,3.3),(3.3/(1.1+2.2)))",1.0), + test_t("equal($f13(1.1,2.2,3.3),(3.3/(1.1-2.2)))",1.0), + test_t("equal($f14(1.1,2.2,3.3),(3.3/(1.1*2.2)))",1.0), + test_t("equal($f15(1.1,2.2,3.3),(3.3/(1.1/2.2)))",1.0), + test_t("equal($f16(1.1,2.2,3.3),(3.3-(1.1/2.2)))",1.0), + test_t("equal($f17(1.1,2.2,3.3),(3.3-(1.1/2.2)))",1.0), + test_t("equal($f18(1.1,2.2,3.3,4.4),(4.4+((1.1+2.2)/3.3)))",1.0), + test_t("equal($f19(1.1,2.2,3.3,4.4),(4.4+((1.1+2.2)*3.3)))",1.0), + test_t("equal($f20(1.1,2.2,3.3,4.4),(4.4+((1.1-2.2)/3.3)))",1.0), + test_t("equal($f21(1.1,2.2,3.3,4.4),(4.4+((1.1-2.2)*3.3)))",1.0), + test_t("equal($f22(1.1,2.2,3.3,4.4),(4.4+((1.1*2.2)/3.3)))",1.0), + test_t("equal($f23(1.1,2.2,3.3,4.4),(4.4+((1.1*2.2)*3.3)))",1.0), + test_t("equal($f24(1.1,2.2,3.3,4.4),(4.4+((1.1/2.2)+3.3)))",1.0), + test_t("equal($f25(1.1,2.2,3.3,4.4),(4.4+((1.1/2.2)/3.3)))",1.0), + test_t("equal($f26(1.1,2.2,3.3,4.4),(4.4+((1.1/2.2)*3.3)))",1.0), + test_t("equal($f27(1.1,2.2,3.3,4.4),(4.4-((1.1+2.2)/3.3)))",1.0), + test_t("equal($f28(1.1,2.2,3.3,4.4),(4.4-((1.1+2.2)*3.3)))",1.0), + test_t("equal($f29(1.1,2.2,3.3,4.4),(4.4-((1.1-2.2)/3.3)))",1.0), + test_t("equal($f30(1.1,2.2,3.3,4.4),(4.4-((1.1-2.2)*3.3)))",1.0), + test_t("equal($f31(1.1,2.2,3.3,4.4),(4.4-((1.1*2.2)/3.3)))",1.0), + test_t("equal($f32(1.1,2.2,3.3,4.4),(4.4-((1.1*2.2)*3.3)))",1.0), + test_t("equal($f33(1.1,2.2,3.3,4.4),(4.4-((1.1/2.2)/3.3)))",1.0), + test_t("equal($f34(1.1,2.2,3.3,4.4),(4.4-((1.1/2.2)*3.3)))",1.0), + test_t("equal($f35(1.1,2.2,3.3,4.4),(((1.1+2.2)*3.3)-4.4))",1.0), + test_t("equal($f36(1.1,2.2,3.3,4.4),(((1.1-2.2)*3.3)-4.4))",1.0), + test_t("equal($f37(1.1,2.2,3.3,4.4),(((1.1*2.2)*3.3)-4.4))",1.0), + test_t("equal($f38(1.1,2.2,3.3,4.4),(((1.1/2.2)*3.3)-4.4))",1.0), + test_t("equal($f39(1.1,2.2,3.3,4.4),(((1.1+2.2)/3.3)-4.4))",1.0), + test_t("equal($f40(1.1,2.2,3.3,4.4),(((1.1-2.2)/3.3)-4.4))",1.0), + test_t("equal($f41(1.1,2.2,3.3,4.4),(((1.1*2.2)/3.3)-4.4))",1.0), + test_t("equal($f42(1.1,2.2,3.3,4.4),(((1.1/2.2)/3.3)-4.4))",1.0), + test_t("1+2+3+4+5+6+7+8+9+0",45.0), + test_t("1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0",45.0), + test_t("1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0 + 9.0 + 0.0",45.0), + test_t("(1+2)+(3+4)+(5+6)+(7+8)+(9+0)",45.0), + test_t("(1-2)+(3-4)+(5-6)+(7-8)+(9-0)",+5.0), + test_t("(1+2)-(3+4)-(5+6)-(7+8)-(9+0)",-39.0), + test_t("(1.0+2.0)+(3.0+4.0)+(5.0+6.0)+(7.0+8.0)+(9.0+0.0)",45.0), + test_t("(1.0-2.0)+(3.0-4.0)+(5.0-6.0)+(7.0-8.0)+(9.0-0.0)",+5.0), + test_t("(1.0+2.0)-(3.0+4.0)-(5.0+6.0)-(7.0+8.0)-(9.0+0.0)",-39.0), + test_t("[(1.0+2.0)+[3.0+4.0]+(5.0+6.0)]+([7.0+8.0]+(9.0+0.0))",45.0), + test_t("([1.0-2.0]+(3.0-4.0)+[5.0-6.0])+[(7.0-8.0)+[9.0-0.0]]",+5.0), + test_t("((1.0+2.0))-[(3.0+4.0)]-((5.0+6.0))-[(7.0+8.0)]-((9.0+0.0))",-39.0), + test_t("{[(1.0+2.0)+[3.0+4.0]+({5.0+6.0})]}+({[7.0+8.0]+(9.0+0.0)})",45.0), + test_t("{([1.0-2.0]+(3.0-4.0)+[5.0-6.0])}+[({+7.0}-{+8.0})+[{+9.0-0.0}]]",+5.0), + test_t("((+1.0+2.0))-[({+3.0+4.0})]-(({+5.0+6.0}))-[({+7.0}+8.0)]-(({+9.0}+{0.0}))",-39.0), + test_t("1+2-3*4/5+6-7*8/9+0",0.37777777777777777778), + test_t("1.1+2.2-3.3*4.4/5.5+6.6-7.7*8.8/9.9+0.0",0.41555555555555555556), + test_t("(1+2)-(3*4)/(5+6)-(7*8)/(9+0)",-4.31313131313131313131), + test_t("1/1+1/2+1/3+1/4+1/5+1/6+1/7+1/8+1/9",2.82896825396825396825), + test_t("(1/1)+(1/2)+(1/3)+(1/4)+(1/5)+(1/6)+(1/7)+(1/8)+(1/9)",2.82896825396825396825), + test_t("1.0/1.0+1.0/2.0+1.0/3.0+1.0/4.0+1.0/5.0+1.0/6.0+1.0/7.0+1.0/8.0+1.0/9",2.82896825396825396825), + test_t("(1.0/1.0)+(1.0/2.0)+(1.0/3.0)+(1.0/4.0)+(1.0/5.0)+(1.0/6.0)+(1.0/7.0)+(1.0/8.0)+(1.0/9)",2.82896825396825396825), + test_t("1/1*1/2*1/3*1/4*1/5*1/6*1/7*1/8*1/9",0.00000275573192239859), + test_t("(1/1)*(1/2)*(1/3)*(1/4)*(1/5)*(1/6)*(1/7)*(1/8)*(1/9)",0.00000275573192239859), + test_t("1.0/1.0*1.0/2.0*1.0/3.0*1.0/4.0*1.0/5.0*1.0/6.0*1.0/7.0*1.0/8.0*1.0/9",0.00000275573192239859), + test_t("(1.0/1.0)*(1.0/2.0)*(1.0/3.0)*(1.0/4.0)*(1.0/5.0)*(1.0/6.0)*(1.0/7.0)*(1.0/8.0)*(1.0/9)",0.00000275573192239859) + }; + +static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_t); + +template +inline bool not_equal(const T& t1, + const T& t2, + const T& epsilon = 0.0000000001/*std::numeric_limits::epsilon()*/) +{ + if (t1 != t1) return true; + if (t2 != t2) return true; + return std::abs(t1 - t2) > (std::max(T(1.0),std::max(std::abs(t1),std::abs(t2))) * epsilon); +} + +template +inline bool test_expression(const std::string& expression_string, const T& expected_result) +{ + exprtk::expression expression; + exprtk::symbol_table symbol_table; + + symbol_table.add_constants(); + expression.register_symbol_table(symbol_table); + + { + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "test_expression() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + } + + T result = expression.value(); + if (not_equal(result,expected_result)) + { + printf("Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", + expression_string.c_str(), + expected_result, + result); + return false; + } + return true; +} + +template +inline bool run_test0() +{ + const std::size_t rounds = 10000; + for (std::size_t r = 0; r < rounds; ++r) + { + for (std::size_t i = 0; i < test_list_size; ++i) + { + if (!test_expression(test_list[i].first,T(test_list[i].second))) + return false; + } + } + return true; +} + +template +inline bool run_test1() +{ + std::string expression_string = "a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+w+x+y+z+" + "A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+" + "aa+bb+cc+dd+ee+ff+gg+hh+ii+jj+kk+ll+mm+nn+oo+pp+qq+rr+ss+tt+uu+vv+ww+xx+yy+zz+" + "AA+BB+CC+DD+EE+FF+GG+HH+II+JJ+KK+LL+MM+NN+OO+PP+QQ+RR+SS+TT+UU+VV+WW+XX+YY+ZZ+" + "Aa+Bb+Cc+Dd+Ee+Ff+Gg+Hh+Ii+Jj+Kk+Ll+Mm+Nn+Oo+Pp+Qq+Rr+Ss+Tt+Uu+Vv+Ww+Xx+Yy+Zz+" + "a0+b1+c2+d3+e4+f5+g6+h7+i8+j9+k0+l1+m2+n3+o4+p5+q6+r7+s8+t9+u0+v1+w2+x3+y4+z5+" + "A0+B1+C2+D3+E4+F5+G6+H7+I8+J9+K0+L1+M2+N3+O4+P5+Q6+R7+S8+T9+U0+V1+W2+X3+Y4+Z5+" + "aa0+bb1+cc2+dd3+ee4+ff5+gg6+hh7+ii8+jj9+kk0+ll1+mm2+nn3+oo4+pp5+qq6+rr7+ss8+tt9+uu0+vv1+ww2+xx3+yy4+zz5+" + "AA0+BB1+CC2+DD3+EE4+FF5+GG6+HH7+II8+JJ9+KK0+LL1+MM2+NN3+OO4+PP5+QQ6+RR7+SS8+TT9+UU0+VV1+WW2+XX3+YY4+ZZ5+" + "Aa0+Bb1+Cc2+Dd3+Ee4+Ff5+Gg6+Hh7+Ii8+Jj9+Kk0+Ll1+Mm2+Nn3+Oo4+Pp5+Qq6+Rr7+Ss8+Tt9+Uu0+Vv1+Ww2+Xx3+Yy4+Zz5"; + + static const std::string variable_list[] = + { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", + "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", + "Z", "aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj", "kk", "ll", "mm", + "nn", "oo", "pp", "qq", "rr", "ss", "tt", "uu", "vv", "ww", "xx", "yy", "zz", "AA", + "BB", "CC", "DD", "EE", "FF", "GG", "HH", "II", "JJ", "KK", "LL", "MM", "NN", "OO", + "PP", "QQ", "RR", "SS", "TT", "UU", "VV", "WW", "XX", "YY", "ZZ", "Aa", "Bb", "Cc", + "Dd", "Ee", "Ff", "Gg", "Hh", "Ii", "Jj", "Kk", "Ll", "Mm", "Nn", "Oo", "Pp", "Qq", + "Rr", "Ss", "Tt", "Uu", "Vv", "Ww", "Xx", "Yy", "Zz", "a0", "b1", "c2", "d3", "e4", + "f5", "g6", "h7", "i8", "j9", "k0", "l1", "m2", "n3", "o4", "p5", "q6", "r7", "s8", + "t9", "u0", "v1", "w2", "x3", "y4", "z5", "A0", "B1", "C2", "D3", "E4", "F5", "G6", + "H7", "I8", "J9", "K0", "L1", "M2", "N3", "O4", "P5", "Q6", "R7", "S8", "T9", "U0", + "V1", "W2", "X3", "Y4", "Z5", "aa0", "bb1", "cc2", "dd3", "ee4", "ff5", "gg6", "hh7", + "ii8", "jj9", "kk0", "ll1", "mm2", "nn3", "oo4", "pp5", "qq6", "rr7", "ss8", "tt9", + "uu0", "vv1", "ww2", "xx3", "yy4", "zz5", "AA0", "BB1", "CC2", "DD3", "EE4", "FF5", + "GG6", "HH7", "II8", "JJ9", "KK0", "LL1", "MM2", "NN3", "OO4", "PP5", "QQ6", "RR7", + "SS8", "TT9", "UU0", "VV1", "WW2", "XX3", "YY4", "ZZ5", "Aa0", "Bb1", "Cc2", "Dd3", + "Ee4", "Ff5", "Gg6", "Hh7", "Ii8", "Jj9", "Kk0", "Ll1", "Mm2", "Nn3", "Oo4", "Pp5", + "Qq6", "Rr7", "Ss8", "Tt9", "Uu0", "Vv1", "Ww2", "Xx3", "Yy4", "Zz5" + }; + + static const std::size_t variable_list_size = sizeof(variable_list) / sizeof(std::string); + + exprtk::expression expression; + exprtk::symbol_table symbol_table; + + std::vector v; + v.resize(variable_list_size); + + for (std::size_t i = 0; i < variable_list_size; ++i) + { + v[i] = i; + symbol_table.add_variable(variable_list[i],v[i]); + } + + symbol_table.add_constants(); + + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "run_test1() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + return true; +} + +template +inline T clamp(const T& l, const T& v, const T& u) +{ + return (v < l) ? l : ((v > u) ? u : v); +} + +template +inline bool run_test2() +{ + std::string expression_string = "clamp(-1.0,sin(2 * pi * x) + cos(y / 2 * pi),+1.0)"; + + exprtk::expression expression; + exprtk::symbol_table symbol_table; + + T x = T(-1000.0); + T y = T(-1000.0); + + symbol_table.add_variable("x",x); + symbol_table.add_variable("y",y); + symbol_table.add_constants(); + + expression.register_symbol_table(symbol_table); + + { + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "run_test2() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + } + + const T pi = T(3.14159265358979323846); + + while ((x <= T(+1000.0)) && (y <= T(+1000.0))) + { + T result1 = expression.value(); + T result2 = clamp(-1.0,std::sin(2 * pi * x) + std::cos(y / 2 * pi),+1.0); + if (not_equal(result1,result2)) + { + printf("run_test2() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f x:%19.15f\ty:%19.15f\n", + expression_string.c_str(), + result1, + result2, + x, + y); + return false; + } + x += 0.001; + y += 0.001; + } + return true; +} + +template +inline bool run_test3() +{ + typedef exprtk::expression expression_t; + std::string expression_string = "clamp(-1.0,sin(2 * pi * x_var123) + cos(y_var123 / 2 * pi),+1.0)"; + + std::deque expression_list; + exprtk::symbol_table symbol_table; + + T x = T(-1000.0); + T y = T(-1000.0); + + symbol_table.add_variable("x_var123",x); + symbol_table.add_variable("y_var123",y); + symbol_table.add_constants(); + + const std::size_t expression_count = 100; + for (std::size_t i = 0; i < expression_count; ++i) + { + expression_t e; + exprtk::parser parser; + e.register_symbol_table(symbol_table); + + if (!parser.compile(expression_string,e)) + { + std::cout << "run_test3() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + expression_list.push_back(e); + } + + const T pi = T(3.14159265358979323846); + + while ((x <= T(+1000.0)) && (y <= T(+1000.0))) + { + T real_result = clamp(-1.0,std::sin(2 * pi * x) + std::cos(y / 2 * pi),+1.0); + + for (std::size_t i = 0; i < expression_list.size(); ++i) + { + expression_t& expr = expression_list[i]; + T result = expr.value(); + if (not_equal(result,real_result)) + { + printf("run_test3() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f x:%19.15f\ty:%19.15f\tIndex:%d\n", + expression_string.c_str(), + real_result, + result, + x, + y, + static_cast(i)); + return false; + } + } + x += 0.001; + y += 0.001; + } + return true; +} + +template +inline bool run_test4() +{ + typedef exprtk::expression expression_t; + std::string expression_string = "sqrt(1 - (x^2))"; + + T x = T(0.0); + + exprtk::symbol_table symbol_table; + symbol_table.add_variable("x",x); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "run_test4() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + + T total_area1 = exprtk::integrate(expression,x,T(-1.0),T(1.0)); + T total_area2 = exprtk::integrate(expression,"x",T(-1.0),T(1.0)); + const T pi = T(3.14159265358979323846); + + if (not_equal(total_area1,total_area2,0.000001)) + { + printf("run_test4() - Integration Error: area1 != area2\n"); + return false; + } + + if (not_equal(total_area1,pi/T(2.0),0.000001)) + { + printf("run_test4() - Integration Error: Expected: %19.15f\tResult: %19.15f\n", + pi/T(2.0), + total_area1); + return false; + } + return true; +} + +template +inline bool run_test5() +{ + typedef exprtk::expression expression_t; + std::string expression_string = "sin(2*x)"; + + T x = T(0.0); + + exprtk::symbol_table symbol_table; + symbol_table.add_variable("x",x); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "run_test5() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + + for (x = -100.0; x < 100; x+=0.00001) + { + T result1 = exprtk::derivative(expression,x); + T result2 = exprtk::derivative(expression,"x"); + T real_result = T(2.0) * std::cos(T(2.0) * x); + + if (not_equal(result1,result2,0.000000001)) + { + printf("run_test5() - Derivative Error: result1 != result2\n"); + return false; + } + if (not_equal(result1,real_result,0.000000001)) + { + printf("run_test5() - Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", + x, + real_result, + result1); + return false; + } + } + return true; +} + +template +inline bool run_test6() +{ + + static const std::string expr_str[] = + { + "x", "y", "z", "w", "u", + "x + y + z + w + u", + "x + y / z * w ^ u", + "min(x,y) + min(x,y,z) + min(x,y,z,w) + min(x,y,z,w,y)", + "max(x,y) + max(x,y,z) + max(x,y,z,w) + max(x,y,z,w,y)", + "avg(x,y)", + "avg(x,y,z)", + "avg(x,y,z,w)", + "avg(x,y,z,w,u)", + "(u := u <- min(x:=1,y:=2,z:=3)) == 1", + "equal($f00(x,y,z),((x+y)/z))", + "equal($f01(x,y,z),((x+y)*z))", + "equal($f02(x,y,z),((x-y)/z))", + "equal($f03(x,y,z),((x-y)*z))", + "equal($f04(x,y,z),((x*y)+z))", + "equal($f05(x,y,z),((x*y)-z))", + "equal($f06(x,y,z),((x*y)/z))", + "equal($f07(x,y,z),((x*y)*z))", + "equal($f08(x,y,z),((x/y)+z))", + "equal($f09(x,y,z),((x/y)-z))", + "equal($f10(x,y,z),((x/y)/z))", + "equal($f11(x,y,z),((x/y)*z))", + "equal($f12(x,y,z),(z/(x+y)))", + "equal($f13(x,y,z),(z/(x-y)))", + "equal($f14(x,y,z),(z/(x*y)))", + "equal($f15(x,y,z),(z/(x/y)))", + "equal($f16(x,y,z),(z-(x/y)))", + "equal($f17(x,y,z),(z-(x/y)))", + "equal($f18(x,y,z,w),(w+((x+y)/z)))", + "equal($f19(x,y,z,w),(w+((x+y)*z)))", + "equal($f20(x,y,z,w),(w+((x-y)/z)))", + "equal($f21(x,y,z,w),(w+((x-y)*z)))", + "equal($f22(x,y,z,w),(w+((x*y)/z)))", + "equal($f23(x,y,z,w),(w+((x*y)*z)))", + "equal($f24(x,y,z,w),(w+((x/y)+z)))", + "equal($f25(x,y,z,w),(w+((x/y)/z)))", + "equal($f26(x,y,z,w),(w+((x/y)*z)))", + "equal($f27(x,y,z,w),(w-((x+y)/z)))", + "equal($f28(x,y,z,w),(w-((x+y)*z)))", + "equal($f29(x,y,z,w),(w-((x-y)/z)))", + "equal($f30(x,y,z,w),(w-((x-y)*z)))", + "equal($f31(x,y,z,w),(w-((x*y)/z)))", + "equal($f32(x,y,z,w),(w-((x*y)*z)))", + "equal($f33(x,y,z,w),(w-((x/y)/z)))", + "equal($f34(x,y,z,w),(w-((x/y)*z)))", + "equal($f35(x,y,z,w),(((x+y)*z)-w))", + "equal($f36(x,y,z,w),(((x-y)*z)-w))", + "equal($f37(x,y,z,w),(((x*y)*z)-w))", + "equal($f38(x,y,z,w),(((x/y)*z)-w))", + "equal($f39(x,y,z,w),(((x+y)/z)-w))", + "equal($f40(x,y,z,w),(((x-y)/z)-w))", + "equal($f41(x,y,z,w),(((x*y)/z)-w))", + "equal($f42(x,y,z,w),(((x/y)/z)-w))", + }; + static const std::size_t expr_str_size = sizeof(expr_str) / sizeof(std::string); + + static const std::size_t rounds = 1000; + for (std::size_t i = 0; i < rounds; ++i) + { + for (std::size_t j = 0; j < expr_str_size; ++j) + { + typedef exprtk::expression expression_t; + + T x = T(1.0); + T y = T(2.0); + T z = T(3.0); + T w = T(4.0); + T u = T(5.0); + + exprtk::symbol_table symbol_table; + 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); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expr_str[j],expression)) + { + std::cout << "run_test6() - Error: " << parser.error() << "\tExpression: " << expr_str[j] << std::endl; + return false; + } + expression.value(); + } + } + return true; +} + +template +struct myfunc : public exprtk::ifunction +{ + myfunc() : exprtk::ifunction(2) {} + + inline T operator()(const T& v1, const T& v2) + { + return T(1) + (v1 * v2) / T(3); + } +}; + +template +inline bool run_test7() +{ + static const std::size_t rounds = 100000; + for (std::size_t i = 0; i < rounds; ++i) + { + typedef exprtk::expression expression_t; + std::string expression_string = "myfunc0(sin(x*pi),y/2)+" + "myfunc1(sin(x*pi),y/2)+" + "myfunc2(sin(x*pi),y/2)+" + "myfunc3(sin(x*pi),y/2)+" + "myfunc4(sin(x*pi),y/2)+" + "myfunc5(sin(x*pi),y/2)+" + "myfunc6(sin(x*pi),y/2)+" + "myfunc7(sin(x*pi),y/2)+" + "myfunc8(sin(x*pi),y/2)+" + "myfunc9(sin(x*pi),y/2)"; + + T x = T(1.0); + T y = T(2.0); + myfunc mf; + + exprtk::symbol_table symbol_table; + symbol_table.add_variable("x",x); + symbol_table.add_variable("y",y); + symbol_table.add_function("myfunc0",mf); + symbol_table.add_function("myfunc1",mf); + symbol_table.add_function("myfunc2",mf); + symbol_table.add_function("myfunc3",mf); + symbol_table.add_function("myfunc4",mf); + symbol_table.add_function("myfunc5",mf); + symbol_table.add_function("myfunc6",mf); + symbol_table.add_function("myfunc7",mf); + symbol_table.add_function("myfunc8",mf); + symbol_table.add_function("myfunc9",mf); + symbol_table.add_constants(); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expression_string,expression)) + { + std::cout << "run_test7() - Error: " << parser.error() << "\tExpression: " << expression_string << std::endl; + return false; + } + T result = expression.value(); + const T pi = T(3.14159265358979323846); + if (not_equal(result,(T(1)+(std::sin(x*pi)*(y/T(2)))/T(3)) * 10.0,0.000001)) + { + printf("run_test7() - Error Expected: %19.15f\tResult: %19.15f\n", + T(1)+(std::sin(x*pi)*(y/T(2)))/T(3), + result); + return false; + } + } + return true; +} + +int main() +{ + return ( + run_test0() && + run_test1() && + run_test2() && + run_test3() && + run_test4() && + run_test5() && + run_test6() && + run_test7() + ) + ? 0 : 1; + } diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..18833ca --- /dev/null +++ b/readme.txt @@ -0,0 +1,48 @@ +C++ Mathematical Expression Toolkit Library + +[INTRODUCTION] +The C++ Mathematical Expression Library (ExprTk) is a simple to use, +easy to integrate and extremely efficient mathematical expression +parsing and evaluation engine. The parsing engine supports various +kinds of functional, logic processing semantics and is very easily +extendible. + + +[COPYRIGHT NOTICE] +Free use of the 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 + + +[DOWNLOADS & UPDATES] +All updates and the most recent version of the C++ Mathematical +Expression Library can be found at: +(1) http://www.partow.net/programming/exprtk/index.html + + +[INSTALLATION] +(1) exprtk.hpp should be placed in a project or system include path +(e.g: /usr/include/). + + +[COMPILATION] +(1) For a complete build: make clean all +(2) For a PGO build: make clean pgo +(3) To strip executables: make strip_bin + + +[COMPILER COMPATIBILITY] +(*) GNU Compiler Collection (4.3+) +(*) Intel® C++ Compiler (9.x+) +(*) Clang/LLVM (1.1+) +(*) Microsoft Visual Studio C++ Compiler (8.1+) +(*) Comeau C++ Compiler (4.3+) + + +[FILES] +(00) Makefile +(01) readme.txt +(02) exprtk.hpp +(03) exprtk_test.cpp