LCOV - code coverage report
Current view: top level - ezlibs - ezExpr.hpp (source / functions) Coverage Total Hit
Test: Coverage (llvm-cov → lcov → genhtml) Lines: 86.8 % 560 486
Test Date: 2025-09-16 22:55:37 Functions: 81.3 % 107 87
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 86.2 % 218 188

             Branch data     Line data    Source code
       1                 :             : #pragma once
       2                 :             : 
       3                 :             : /*
       4                 :             : MIT License
       5                 :             : 
       6                 :             : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
       7                 :             : 
       8                 :             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :             : of this software and associated documentation files (the "Software"), to deal
      10                 :             : in the Software without restriction, including without limitation the rights
      11                 :             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :             : copies of the Software, and to permit persons to whom the Software is
      13                 :             : furnished to do so, subject to the following conditions:
      14                 :             : 
      15                 :             : The above copyright notice and this permission notice shall be included in all
      16                 :             : copies or substantial portions of the Software.
      17                 :             : 
      18                 :             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24                 :             : SOFTWARE.
      25                 :             : */
      26                 :             : 
      27                 :             : // ezExpr is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
      28                 :             : 
      29                 :             : #ifndef _USE_MATH_DEFINES
      30                 :             : #define _USE_MATH_DEFINES
      31                 :             : #endif  // _USE_MATH_DEFINES
      32                 :             : 
      33                 :             : #include <array>
      34                 :             : #include <math.h>
      35                 :             : #include <string>
      36                 :             : #include <vector>
      37                 :             : #include <limits>
      38                 :             : #include <memory>
      39                 :             : #include <chrono>
      40                 :             : #include <cstring>
      41                 :             : #include <sstream>
      42                 :             : #include <iostream>
      43                 :             : #include <functional>
      44                 :             : #include <unordered_map>
      45                 :             : 
      46                 :             : #ifndef DONT_DEFINE_DEFAULT_BUILTINS
      47                 :             : #define DEFINE_DEFAULT_BUILTINS
      48                 :             : #endif  // DONT_DEFINE_DEFAULT_BUILTINS
      49                 :             : 
      50                 :             : #ifndef DONT_USE_PERFO_MEASURING
      51                 :             : #define USE_PERFO_MEASURING
      52                 :             : #endif  // DONT_USE_PERFO_MEASURING
      53                 :             : 
      54                 :             : namespace ez {
      55                 :             : 
      56                 :             : // Class that encapsulates a string
      57                 :             : class String {
      58                 :             : private:
      59                 :             :     char* m_Data;  // Pointer to the character string
      60                 :             :     size_t m_Length;  // Length of the string
      61                 :             : 
      62                 :             : public:
      63                 :             :     // Default constructor: initializes members to nullptr and length to 0
      64                 :         997 :     String() : m_Data(nullptr), m_Length(0) {}
      65                 :             : 
      66                 :             :     // Constructor from a std::string
      67                 :          24 :     String(const ::std::string& str) {
      68         [ +  - ]:          24 :         if (!str.empty()) {
      69                 :          24 :             m_Length = str.size();
      70                 :          24 :             m_Data = new char[m_Length + 1];
      71                 :          24 :             ::std::memcpy(m_Data, str.data(), m_Length + 1);
      72                 :          24 :         } else {
      73                 :           0 :             m_Data = nullptr;
      74                 :           0 :             m_Length = 0;
      75                 :           0 :         }
      76                 :          24 :     }
      77                 :             : 
      78                 :             :     // Constructor from a C-style string (const char*)
      79                 :    26010311 :     String(const char* str) {
      80         [ +  - ]:    26010311 :         if (str) {
      81                 :    26010311 :             m_Length = ::std::strlen(str);
      82                 :    26010311 :             m_Data = new char[m_Length + 1];
      83                 :    26010311 :             ::std::memcpy(m_Data, str, m_Length + 1);
      84                 :    26010311 :         } else {
      85                 :           0 :             m_Data = nullptr;
      86                 :           0 :             m_Length = 0;
      87                 :           0 :         }
      88                 :    26010311 :     }
      89                 :             : 
      90                 :             :     // Constructor from a single character
      91                 :         815 :     String(char ch) {
      92                 :         815 :         m_Length = 1;
      93                 :         815 :         m_Data = new char[2];
      94                 :         815 :         m_Data[0] = ch;
      95                 :         815 :         m_Data[1] = '\0';
      96                 :         815 :     }
      97                 :             : 
      98                 :             :     // Copy constructor
      99                 :        9848 :     String(const String& other) {
     100         [ +  + ]:        9848 :         if (other.m_Data) {
     101                 :        9768 :             m_Length = other.m_Length;
     102                 :        9768 :             m_Data = new char[m_Length + 1];
     103                 :        9768 :             ::std::memcpy(m_Data, other.m_Data, m_Length + 1);
     104                 :        9768 :         } else {
     105                 :          80 :             m_Data = nullptr;
     106                 :          80 :             m_Length = 0;
     107                 :          80 :         }
     108                 :        9848 :     }
     109                 :             : 
     110                 :             :     // Move constructor
     111                 :        5392 :     String(String&& other) noexcept : m_Data(other.m_Data), m_Length(other.m_Length) {
     112                 :        5392 :         other.m_Data = nullptr;
     113                 :        5392 :         other.m_Length = 0;
     114                 :        5392 :     }
     115                 :             : 
     116                 :             :     // Copy assignment operator
     117                 :    84000079 :     char operator[](const size_t idx) {
     118                 :    84000079 :         return m_Data[idx];  // no verification
     119                 :    84000079 :     }
     120                 :             : 
     121                 :             :     // Copy assignment operator
     122                 :         558 :     String& operator=(const String& other) {
     123         [ +  - ]:         558 :         if (this != &other) {  // Avoid self-assignment
     124                 :         558 :             delete[] m_Data;  // Free existing memory
     125         [ +  - ]:         558 :             if (other.m_Data) {
     126                 :         558 :                 m_Length = other.m_Length;
     127                 :         558 :                 m_Data = new char[m_Length + 1];
     128                 :         558 :                 ::std::memcpy(m_Data, other.m_Data, m_Length + 1);
     129                 :         558 :             } else {
     130                 :           0 :                 m_Data = nullptr;
     131                 :           0 :                 m_Length = 0;
     132                 :           0 :             }
     133                 :         558 :         }
     134                 :         558 :         return *this;
     135                 :         558 :     }
     136                 :             : 
     137                 :             :     // Move assignment operator
     138                 :         677 :     String& operator=(String&& other) noexcept {
     139         [ +  - ]:         677 :         if (this != &other) {  // Avoid self-assignment
     140                 :         677 :             delete[] m_Data;  // Free existing memory
     141                 :         677 :             m_Data = other.m_Data;
     142                 :         677 :             m_Length = other.m_Length;
     143                 :         677 :             other.m_Data = nullptr;  // Reset moved object
     144                 :         677 :             other.m_Length = 0;
     145                 :         677 :         }
     146                 :         677 :         return *this;
     147                 :         677 :     }
     148                 :             : 
     149                 :             :     // Destructor: releases allocated memory
     150                 :    26027387 :     ~String() { delete[] m_Data; }
     151                 :             : 
     152                 :             :     // Static method to create a String from a double
     153                 :         284 :     static String fromDouble(double value) {
     154                 :         284 :         char buffer[32];
     155                 :         284 :         ::std::snprintf(buffer, sizeof(buffer), "%.16g", value);  // Convert double to string
     156                 :         284 :         return String(buffer);
     157                 :         284 :     }
     158                 :             : 
     159                 :             :     // Method to create a std::string from a String
     160                 :           0 :     ::std::string to_string() const {
     161                 :           0 :         if (m_Data == nullptr) {
     162                 :           0 :             return {};
     163                 :           0 :         }
     164                 :           0 :         return std::string(m_Data, m_Length);
     165                 :           0 :     }
     166                 :             : 
     167                 :             :     // Equality operator
     168                 :    88005411 :     bool operator==(const String& other) const {
     169         [ +  + ]:    88005411 :         if (m_Length != other.m_Length) {
     170                 :        3881 :             return false;  // If lengths are different, strings are different
     171                 :        3881 :         }
     172                 :    88001530 :         return ::std::memcmp(m_Data, other.m_Data, m_Length) == 0;  // Compare data
     173                 :    88005411 :     }
     174                 :             : 
     175                 :             :     // Inequality operator
     176                 :           0 :     bool operator!=(const String& other) const { return !(*this == other); }
     177                 :             : 
     178                 :             :     // Equality operator with a C-style string
     179                 :         558 :     bool operator==(const char* str) const { return ::std::strcmp(m_Data, str) == 0; }
     180                 :             : 
     181                 :             :     // Inequality operator with a C-style string
     182                 :           0 :     bool operator!=(const char* str) const { return ::std::strcmp(m_Data, str) != 0; }
     183                 :             : 
     184                 :             :     // Returns the encapsulated C-style string
     185         [ +  - ]:    88028110 :     const char* c_str() const { return m_Data ? m_Data : ""; }
     186                 :             : 
     187                 :             :     // Returns the length of the string
     188                 :   232134615 :     size_t length() const { return m_Length; }
     189                 :             : 
     190                 :             :     // Concatenation operator with another String
     191                 :         484 :     String operator+(const String& other) const {
     192                 :         484 :         size_t newLength = m_Length + other.m_Length;  // Calculate new length
     193                 :         484 :         char* newData = new char[newLength + 1];  // Allocate memory
     194                 :         484 :         ::std::memcpy(newData, m_Data, m_Length);  // Copy first string
     195                 :         484 :         ::std::memcpy(newData + m_Length, other.m_Data, other.m_Length + 1);  // Copy second string
     196                 :         484 :         return String(newData);  // Return new String
     197                 :         484 :     }
     198                 :             : 
     199                 :             :     // Concatenation operator with a C-style string
     200                 :         145 :     String operator+(const char* str) const {
     201                 :         145 :         size_t strLength = ::std::strlen(str);  // Calculate length of str
     202                 :         145 :         size_t newLength = m_Length + strLength;  // Calculate new length
     203                 :         145 :         char* newData = new char[newLength + 1];  // Allocate memory
     204                 :         145 :         ::std::memcpy(newData, m_Data, m_Length);  // Copy first string
     205                 :         145 :         ::std::memcpy(newData + m_Length, str, strLength + 1);  // Copy str
     206                 :         145 :         return String(newData);  // Return new String
     207                 :         145 :     }
     208                 :             : 
     209                 :             :     // Stream output operator overload for displaying the string
     210                 :           0 :     friend ::std::ostream& operator<<(::std::ostream& os, const String& str) {
     211                 :           0 :         os << str.c_str();  // Write the string to the stream
     212                 :           0 :         return os;
     213                 :           0 :     }
     214                 :             : };
     215                 :             : 
     216                 :             : }  // namespace ez
     217                 :             : 
     218                 :             : namespace std {
     219                 :             : 
     220                 :             : // Specialization of std::hash to use ez::String as a key in containers
     221                 :             : template <>
     222                 :             : struct hash<ez::String> {
     223                 :    88027340 :     size_t operator()(const ez::String& s) const noexcept {
     224                 :    88027340 :         size_t hash = 0;
     225                 :    88027340 :         const char* data = s.c_str();
     226         [ +  + ]:   232134557 :         for (size_t i = 0; i < s.length(); ++i) {
     227                 :   144107217 :             hash = hash * 31 + static_cast<size_t>(data[i]);  // Simple hash calculation
     228                 :   144107217 :         }
     229                 :    88027340 :         return hash;
     230                 :    88027340 :     }
     231                 :             : };
     232                 :             : 
     233                 :             : }  // namespace std
     234                 :             : 
     235                 :             : namespace ez {
     236                 :             : 
     237                 :             : // Overload of the + operator to allow concatenation of const char* and ez::String
     238                 :          29 : static inline String operator+(const char* lhs, const String& rhs) {
     239                 :          29 :     size_t lhsLength = ::std::strlen(lhs);  // Calculate length of lhs
     240                 :          29 :     size_t newLength = lhsLength + rhs.length();  // Calculate new length
     241                 :          29 :     char* newData = new char[newLength + 1];  // Allocate memory
     242                 :          29 :     ::std::memcpy(newData, lhs, lhsLength);  // Copy lhs
     243                 :          29 :     ::std::memcpy(newData + lhsLength, rhs.c_str(), rhs.length() + 1);  // Copy rhs
     244                 :          29 :     return String(newData);  // Return new String
     245                 :          29 : }
     246                 :             : 
     247                 :             : // Definition of aliases for functions with different numbers of arguments
     248                 :             : typedef ::std::function<double(double)> UnaryFunctor;
     249                 :             : typedef ::std::function<double(double, double)> BinaryFunctor;
     250                 :             : typedef ::std::function<double(double, double, double)> TernaryFunctor;
     251                 :             : typedef ::std::unordered_map<String, double> VarContainer;
     252                 :             : typedef ::std::unordered_map<String, double> ConstantContainer;
     253                 :             : 
     254                 :             : // Structure representing a function with its number of arguments and behavior
     255                 :             : struct Function {
     256                 :             :     UnaryFunctor unaryFunctor = nullptr;
     257                 :             :     BinaryFunctor binaryFunctor = nullptr;
     258                 :             :     TernaryFunctor ternaryFunctor = nullptr;
     259                 :             :     size_t argCount = 0U;
     260                 :             : };
     261                 :             : 
     262                 :             : // Container to store available functions
     263                 :             : typedef ::std::unordered_map<String, Function> FunctionContainer;
     264                 :             : 
     265                 :             : // Definition of possible token types
     266                 :             : enum class TokenType { NUMBER = 0, LPAREN, RPAREN, VARIABLE, OPERATOR, FUNCTION, SEPARATOR, Count };
     267                 :             : 
     268                 :             : // Structure representing a token (lexical unit)
     269                 :             : struct Token {
     270                 :             :     TokenType type;
     271                 :             :     String value;
     272                 :             : };
     273                 :             : 
     274                 :             : // Definition of possible error codes
     275                 :             : enum class ErrorCode {
     276                 :             :     NONE = 0,
     277                 :             :     PARSE_ERROR,
     278                 :             :     EVALUATION_NAN,
     279                 :             :     EVALUATION_INF,
     280                 :             :     DIVISION_BY_ZERO,
     281                 :             :     UNKNOWN_NODE_TYPE,
     282                 :             :     EMPTY_PARENTHESIS,
     283                 :             :     VARIABLE_NOT_FOUND,
     284                 :             :     FUNCTION_NOT_FOUND,
     285                 :             :     OPERATOR_NOT_FOUND,
     286                 :             :     UNMATCHED_PARENTHESIS,
     287                 :             :     FUNCTION_WRONG_ARGUMENTS_COUNT,
     288                 :             :     Count
     289                 :             : };
     290                 :             : 
     291                 :             : // Definition of possible node types in a syntax tree
     292                 :             : enum class NodeType { NUMBER = 0, VARIABLE, OPERATOR, FUNCTION, Count };
     293                 :             : 
     294                 :             : // Structure representing a node in the syntax tree
     295                 :             : struct Node {
     296                 :             :     NodeType type = NodeType::NUMBER;
     297                 :             :     String name;
     298                 :             :     double value = 0.0;
     299                 :             :     ::std::array<::std::shared_ptr<Node>, 3> childs{};  // Array of pointers to child nodes
     300                 :             :     ::std::array<double, 3> tmp{};  // for avoid allocation during evaluation
     301                 :             :     size_t childCount = 0;
     302                 :             : };
     303                 :             : 
     304                 :             : // Class to manage exceptions specific to expression evaluation
     305                 :             : class ExprException : public ::std::exception {
     306                 :             : public:
     307                 :          56 :     ExprException(ErrorCode code, const String& message) : m_Code(code), m_Message(message) {}
     308                 :             : 
     309                 :          56 :     ErrorCode getCode() const { return m_Code; }
     310                 :             : 
     311                 :          56 :     const char* what() const noexcept override {
     312                 :          56 :         return m_Message.c_str();  // Return the error message
     313                 :          56 :     }
     314                 :             : 
     315                 :             : private:
     316                 :             :     ErrorCode m_Code;
     317                 :             :     String m_Message;
     318                 :             : };
     319                 :             : 
     320                 :             : // Main class for evaluating mathematical expressions
     321                 :             : class Expr {
     322                 :             : private:
     323                 :             :     String m_Expr;  // Expression to evaluate
     324                 :             :     Node m_RootExpr;  // Root of the syntax tree
     325                 :             :     VarContainer m_ParsedVariables;  // Container for variables found during parsing
     326                 :             :     VarContainer m_DefinedVariables;  // Container for variables defined after parsing
     327                 :             :     ConstantContainer m_Constant;  // Container for constants
     328                 :             :     FunctionContainer m_Functions;  // Container for functions
     329                 :             :     double m_EvalResult = 0.0;  // Evaluation result
     330                 :             :     bool m_Verbose = false;  // Verbose mode
     331                 :             :     ::std::stringstream m_printExpr;  // Stream to print the expression and result
     332                 :             :     ::std::chrono::duration<double, ::std::milli> m_Elapsed;  // Evaluation time
     333                 :             :     ::std::chrono::steady_clock::time_point m_StartTime;
     334                 :             :     ::std::chrono::steady_clock::time_point m_EndTime;
     335                 :             : 
     336                 :             : public:
     337                 :         202 :     Expr() {
     338                 :         202 : #ifdef DEFINE_DEFAULT_BUILTINS
     339                 :         202 :         defineDefaultBuiltins();
     340                 :         202 : #endif  // DEFINE_DEFAULT_BUILTINS
     341                 :         202 :     }
     342                 :             : 
     343                 :         202 :     void defineDefaultBuiltins() {
     344                 :             :         // Initialization of common constants
     345                 :         202 :         addConstant("pi", M_PI);  // PI
     346                 :         202 :         addConstant("e", M_E);  // e
     347                 :             : 
     348                 :             :         // Initialization of common unary functions
     349                 :         202 :         addFunction("-", [](double a) { return -a; });
     350                 :             : 
     351                 :     2000003 :         addFunction("abs", [](double a) { return ::std::abs(a); });
     352                 :         202 :         addFunction("floor", [](double a) { return ::std::floor(a); });
     353                 :         202 :         addFunction("ceil", [](double a) { return ::std::ceil(a); });
     354                 :         202 :         addFunction("round", [](double a) { return ::std::round(a); });
     355                 :             : 
     356                 :         202 :         addFunction("fract", [](double a) { return a - ::std::floor(a); });
     357                 :         202 :         addFunction("sign", [this](double a) { return m_Sign(a); });
     358                 :             : 
     359                 :     4000003 :         addFunction("sin", [](double a) { return ::std::sin(a); });
     360                 :     2000003 :         addFunction("cos", [](double a) { return ::std::cos(a); });
     361                 :         202 :         addFunction("tan", [](double a) { return ::std::tan(a); });
     362                 :             : 
     363                 :         202 :         addFunction("asin", [](double a) { return ::std::asin(a); });
     364                 :         202 :         addFunction("acos", [](double a) { return ::std::acos(a); });
     365                 :         202 :         addFunction("atan", [](double a) { return ::std::atan(a); });
     366                 :             : 
     367                 :         202 :         addFunction("sinh", [](double a) { return ::std::sinh(a); });
     368                 :         202 :         addFunction("cosh", [](double a) { return ::std::cosh(a); });
     369                 :         202 :         addFunction("tanh", [](double a) { return ::std::tanh(a); });
     370                 :             : 
     371                 :         202 :         addFunction("asinh", [](double a) { return ::std::asinh(a); });
     372                 :         202 :         addFunction("acosh", [](double a) { return ::std::acosh(a); });
     373                 :         202 :         addFunction("atanh", [](double a) { return ::std::atanh(a); });
     374                 :             : 
     375                 :         202 :         addFunction("ln", [](double a) { return ::std::log(a); });
     376                 :         202 :         addFunction("log", [](double a) { return ::std::log(a); });
     377                 :         202 :         addFunction("log1p", [](double a) { return ::std::log1p(a); });
     378                 :         202 :         addFunction("logb", [](double a) { return ::std::logb(a); });
     379                 :         202 :         addFunction("log2", [](double a) { return ::std::log2(a); });
     380                 :         202 :         addFunction("log10", [](double a) { return ::std::log10(a); });
     381                 :             : 
     382                 :     4000004 :         addFunction("sqrt", [](double a) { return ::std::sqrt(a); });
     383                 :         202 :         addFunction("exp", [](double a) { return ::std::exp(a); });
     384                 :             : 
     385                 :         202 :         addFunction("fact", [this](double a) { return m_Factorial(a); });
     386                 :             : 
     387                 :         202 :         addFunction("saturate", [this](double a) { return m_Clamp(a, 0.0, 1.0); });
     388                 :             : 
     389                 :             :         // Initialization of common binary functions
     390                 :         202 :         addFunction("mod", [](double a, double b) { return ::std::fmod(a, b); });
     391                 :     4000000 :         addFunction("pow", [](double a, double b) {
     392         [ -  + ]:     4000000 :             if (a < 0.0) {
     393                 :           0 :                 throw ExprException(ErrorCode::EVALUATION_NAN, "Pow base value is negative");
     394                 :           0 :             }
     395                 :     4000000 :             return ::std::pow(a, b);
     396                 :     4000000 :         });
     397                 :         202 :         addFunction("atan2", [](double a, double b) { return ::std::atan2(a, b); });
     398                 :         202 :         addFunction("min", [this](double a, double b) { return m_Min(a, b); });
     399                 :         202 :         addFunction("max", [this](double a, double b) { return m_Max(a, b); });
     400                 :         202 :         addFunction("step", [this](double a, double b) { return m_Step(a, b); });
     401                 :         202 :         addFunction("hypot", [](double a, double b) { return ::std::hypot(a, b); });
     402                 :         202 :         addFunction("smoothabs", [this](double a, double b) { return m_SmoothAbs(a, b); });
     403                 :             : 
     404                 :             :         // Initialization of common ternary functions
     405                 :         202 :         addFunction("clamp", [this](double a, double b, double c) { return m_Clamp(a, b, c); });
     406                 :         202 :         addFunction("lerp", [this](double a, double b, double c) { return m_Mix(a, b, c); });
     407                 :         202 :         addFunction("mix", [this](double a, double b, double c) { return m_Mix(a, b, c); });
     408                 :         202 :         addFunction("smoothstep", [this](double a, double b, double c) { return m_SmoothStep(a, b, c); });
     409                 :         202 :     }
     410                 :             : 
     411                 :             :     // Method to parse an expression
     412                 :         202 :     Expr& parse(const String& vExpr) {
     413                 :         202 :         m_Expr = vExpr;  // Store the expression
     414                 :         202 :         m_ParsedVariables.clear();  // clearing discoverd vairable during parsing
     415                 :         202 :         auto tokens = m_tokenize(m_Expr);  // Tokenize the expression
     416                 :         202 :         size_t pos = 0;
     417                 :         202 :         m_RootExpr = m_parseExpression(tokens, pos, 0);  // Parse the expression to create the syntax tree
     418                 :             : 
     419                 :             :         // Check for remaining tokens after parsing
     420         [ +  + ]:         202 :         if (pos < tokens.size()) {
     421                 :             :             // If the next token is an unmatched closing parenthesis
     422         [ +  + ]:          11 :             if (tokens[pos].value == ")") {
     423                 :           3 :                 throw ExprException(ErrorCode::UNMATCHED_PARENTHESIS, "Unmatched parenthesis found at the end of the expression");
     424                 :           3 :             }
     425                 :             :             // If other tokens remain, which is also abnormal
     426                 :           8 :             throw ExprException(ErrorCode::PARSE_ERROR, "Unexpected token found after complete parsing.");
     427                 :          11 :         }
     428                 :             : 
     429                 :         191 :         return *this;
     430                 :         202 :     }
     431                 :             : 
     432                 :             :     // Method to set the value of a variable
     433                 :    26000000 :     Expr& set(const String& vName, const double vValue, const bool vIfNotExist = false) {
     434         [ -  + ]:    26000000 :         if (vIfNotExist) {
     435         [ #  # ]:           0 :             if (m_DefinedVariables.find(vName) != m_DefinedVariables.end()) {  // If exists
     436                 :           0 :                 return *this;
     437                 :           0 :             }
     438                 :           0 :         }
     439                 :    26000000 :         m_DefinedVariables[vName] = vValue;
     440                 :    26000000 :         return *this;
     441                 :    26000000 :     }
     442                 :             : 
     443                 :             :     // Method to evaluate the expression
     444                 :    24000136 :     Expr& eval() {
     445                 :    24000136 :         m_evalNode(m_RootExpr, m_EvalResult);  // Evaluate the root of the syntax tree
     446                 :    24000136 :         return *this;
     447                 :    24000136 :     }
     448                 :             : 
     449                 :           0 :     Expr& startTime() {
     450                 :           0 :         m_StartTime = ::std::chrono::steady_clock::now();  // Start time
     451                 :           0 :         return *this;
     452                 :           0 :     }
     453                 :             : 
     454                 :           0 :     Expr& endTime() {
     455                 :           0 :         m_EndTime = ::std::chrono::steady_clock::now();  // End time
     456                 :           0 :         return *this;
     457                 :           0 :     }
     458                 :             : 
     459                 :             :     // Returns the evaluation time in milliseconds
     460                 :           0 :     double getEvalTime() {
     461                 :           0 :         m_Elapsed = m_EndTime - m_StartTime;
     462                 :           0 :         return m_Elapsed.count();
     463                 :           0 :     }
     464                 :             : 
     465                 :             :     // Method to add a constant
     466                 :         404 :     Expr& addConstant(const String& vName, const double vValue) {
     467                 :         404 :         m_Constant[vName] = vValue;  // Add the constant
     468                 :         404 :         return *this;
     469                 :         404 :     }
     470                 :             : 
     471                 :             :     // Method to add a unary function
     472                 :        5858 :     Expr& addFunction(const String& vName, const UnaryFunctor& functor) {
     473                 :        5858 :         Function fun;
     474                 :        5858 :         fun.unaryFunctor = functor;
     475                 :        5858 :         fun.argCount = 1;
     476                 :        5858 :         m_Functions[vName] = fun;
     477                 :        5858 :         return *this;
     478                 :        5858 :     }
     479                 :             : 
     480                 :             :     // Method to add a binary function
     481                 :        1616 :     Expr& addFunction(const String& vName, const BinaryFunctor& functor) {
     482                 :        1616 :         Function fun;
     483                 :        1616 :         fun.binaryFunctor = functor;
     484                 :        1616 :         fun.argCount = 2;
     485                 :        1616 :         m_Functions[vName] = fun;
     486                 :        1616 :         return *this;
     487                 :        1616 :     }
     488                 :             : 
     489                 :             :     // Method to add a ternary function
     490                 :         808 :     Expr& addFunction(const String& vName, const TernaryFunctor& functor) {
     491                 :         808 :         Function fun;
     492                 :         808 :         fun.ternaryFunctor = functor;
     493                 :         808 :         fun.argCount = 3;
     494                 :         808 :         m_Functions[vName] = fun;
     495                 :         808 :         return *this;
     496                 :         808 :     }
     497                 :             : 
     498                 :             :     // Method to enable or disable verbose mode
     499                 :          40 :     Expr& setVerbose(bool vVerbose) {
     500                 :          40 :         m_Verbose = vVerbose;
     501                 :          40 :         return *this;
     502                 :          40 :     }
     503                 :             : 
     504                 :             :     // Returns the evaluation result
     505                 :    24000000 :     double getResult() { return m_EvalResult; }
     506                 :             : 
     507                 :             :     // Prints the expression and its result
     508                 :           0 :     Expr& print() {
     509                 :           0 :         m_printExpr = {};
     510                 :           0 :         m_printExpr << "Expr \"" << m_Expr << "\"";
     511                 :           0 :         for (const auto& it : m_DefinedVariables) {
     512                 :           0 :             m_printExpr << ", " << it.first << " = " << it.second;
     513                 :           0 :         }
     514                 :           0 :         m_printExpr << " => " << m_EvalResult;
     515                 :           0 :         ::std::cout << m_printExpr.str() << " (" << getEvalTime() << " ms)" << ::std::endl;
     516                 :           0 :         return *this;
     517                 :           0 :     }
     518                 :             : 
     519                 :             :     // Checks if the evaluation result is close to a given value
     520                 :         122 :     bool check(const double vValue) { return (::std::abs(m_EvalResult - vValue) < 0.001); }
     521                 :             : 
     522                 :             :     // test if a variable found during parsing exist
     523                 :           0 :     bool isParsedVariableExist(const String& vName) { return (m_ParsedVariables.find(vName) != m_ParsedVariables.end()); }
     524                 :             : 
     525                 :             :     // Returns variables as a constant reference
     526                 :           0 :     const VarContainer& getParsedVars() { return m_ParsedVariables; }
     527                 :             : 
     528                 :             :     // Returns variables as a modifiable reference
     529                 :           0 :     VarContainer& getParsedVarsRef() { return m_ParsedVariables; }
     530                 :             : 
     531                 :             :     // test if a defined variable exist
     532                 :           0 :     bool isDefinedVariableExist(const String& vName) { return (m_DefinedVariables.find(vName) != m_ParsedVariables.end()); }
     533                 :             : 
     534                 :             :     // Returns defined variables as a constant reference
     535                 :           0 :     const VarContainer& getDefinedVars() { return m_DefinedVariables; }
     536                 :             : 
     537                 :             :     // Returns defined variables as a modifiable reference
     538                 :           0 :     VarContainer& getDefinedVarsRef() { return m_DefinedVariables; }
     539                 :             : 
     540                 :             : protected:
     541                 :             :     // Tokenize the expression
     542                 :         202 :     ::std::vector<Token> m_tokenize(const String& expr) {
     543                 :         202 :         ::std::vector<Token> tokens;
     544                 :         202 :         ::std::istringstream stream(expr.c_str());
     545                 :         202 :         char ch = 0;
     546         [ +  + ]:        1206 :         while (stream >> ch) {
     547 [ +  + ][ -  + ]:        1004 :             if (::std::isdigit(ch) || ch == '.') {
     548                 :         282 :                 stream.putback(ch);
     549                 :         282 :                 double number;
     550                 :         282 :                 stream >> number;
     551                 :         282 :                 tokens.push_back({TokenType::NUMBER, String::fromDouble(number)});
     552         [ +  + ]:         722 :             } else if (::std::isalpha(ch)) {
     553                 :         201 :                 String identifier(ch);
     554 [ +  + ][ +  + ]:         681 :                 while (stream.get(ch) && ::std::isalnum(ch)) {
     555                 :         480 :                     identifier = identifier + String(ch);
     556                 :         480 :                 }
     557                 :         201 :                 stream.putback(ch);
     558                 :         201 :                 tokens.push_back({TokenType::VARIABLE, identifier});  // Traitement g�n�rique comme identifiant
     559         [ +  + ]:         521 :             } else if (ch == '(') {
     560                 :         165 :                 tokens.push_back({TokenType::LPAREN, String("(")});
     561         [ +  + ]:         356 :             } else if (ch == ')') {
     562                 :         165 :                 tokens.push_back({TokenType::RPAREN, String(")")});
     563         [ +  + ]:         191 :             } else if (ch == ',') {
     564                 :          61 :                 tokens.push_back({TokenType::SEPARATOR, String(",")});
     565                 :         130 :             } else {
     566                 :         130 :                 String op(ch);
     567 [ +  + ][ +  + ]:         134 :                 while (stream.get(ch) && !::std::isalnum(ch) && ch != ' ' && ch != '(' && ch != ')' && ch != ',') {
         [ +  + ][ +  + ]
         [ +  - ][ +  - ]
     568                 :           4 :                     op = op + String(ch);
     569                 :           4 :                 }
     570                 :         130 :                 stream.putback(ch);
     571                 :         130 :                 tokens.push_back({TokenType::OPERATOR, op});
     572                 :         130 :             }
     573                 :        1004 :         }
     574         [ +  + ]:         202 :         if (m_Verbose) {
     575                 :          40 :             m_log("Tokens: ");
     576         [ +  + ]:         143 :             for (const auto& token : tokens) {
     577                 :         143 :                 m_log(token.value + " ");
     578                 :         143 :             }
     579                 :          40 :             m_log("\n");
     580                 :          40 :         }
     581                 :         202 :         return tokens;
     582                 :         202 :     }
     583                 :             : 
     584                 :             :     // Logs messages if verbose mode is enabled
     585                 :         225 :     void m_log(const String& message) {
     586         [ +  - ]:         225 :         if (m_Verbose) {
     587                 :         225 :             ::std::cout << message.c_str();
     588                 :         225 :         }
     589                 :         225 :     }
     590                 :             : 
     591                 :             :     // Evaluate a node in the syntax tree
     592                 :   128000369 :     void m_evalNode(Node& node, double& vOutResult) {
     593                 :   128000369 :         switch (node.type) {
     594         [ +  + ]:    32000197 :             case NodeType::NUMBER: {
     595                 :    32000197 :                 vOutResult = node.value;
     596                 :    32000197 :             } break;
     597         [ +  + ]:    42000020 :             case NodeType::OPERATOR: {
     598                 :    42000020 :                 m_evalNode(*node.childs[0], node.tmp[0]);  // left
     599                 :    42000020 :                 m_evalNode(*node.childs[1], node.tmp[1]);  // right
     600         [ +  + ]:    42000020 :                 if (node.name[0] == '+') {
     601                 :    24000000 :                     vOutResult = node.tmp[0] + node.tmp[1];
     602         [ -  + ]:    24000000 :                 } else if (node.name[0] == '-') {
     603                 :           0 :                     vOutResult = node.tmp[0] - node.tmp[1];
     604         [ +  + ]:    18000020 :                 } else if (node.name[0] == '*') {
     605                 :    12000000 :                     vOutResult = node.tmp[0] * node.tmp[1];
     606         [ +  + ]:    12000000 :                 } else if (node.name[0] == '/') {
     607         [ +  + ]:     6000008 :                     if (node.tmp[1] == 0.0) {
     608                 :           5 :                         throw ExprException(ErrorCode::DIVISION_BY_ZERO, "Division by zero");
     609                 :           5 :                     }
     610                 :     6000003 :                     vOutResult = node.tmp[0] / node.tmp[1];
     611         [ +  + ]:     6000003 :                 } else if (node.name[0] == '^') {
     612         [ +  + ]:           4 :                     if (node.tmp[0] < 0.0) {
     613                 :           1 :                         throw ExprException(ErrorCode::EVALUATION_NAN, "Pow base value is negative");
     614                 :           1 :                     }
     615                 :           3 :                     vOutResult = ::std::pow(node.tmp[0], node.tmp[1]);
     616         [ +  + ]:           8 :                 } else if (node.name[0] == '%') {
     617         [ +  + ]:           4 :                     if (node.tmp[1] == 0.0) {
     618                 :           1 :                         throw ExprException(ErrorCode::DIVISION_BY_ZERO, "Division by zero");
     619                 :           1 :                     }
     620                 :           3 :                     vOutResult = ::std::fmod(node.tmp[0], node.tmp[1]);
     621                 :           3 :                 }
     622                 :    42000020 :             } break;
     623         [ +  + ]:    42000013 :             case NodeType::VARIABLE: {
     624                 :    38000003 :                 auto it = m_DefinedVariables.find(node.name);
     625         [ +  + ]:    38000003 :                 if (it == m_DefinedVariables.end()) {
     626                 :           3 :                     throw ExprException(ErrorCode::VARIABLE_NOT_FOUND, "Variable not found: " + node.name);
     627                 :           3 :                 }
     628                 :    38000000 :                 vOutResult = it->second;
     629                 :    38000000 :             } break;
     630         [ +  + ]:    16000149 :             case NodeType::FUNCTION: {
     631                 :    16000149 :                 auto it = m_Functions.find(node.name);
     632         [ +  + ]:    16000149 :                 if (it == m_Functions.end()) {
     633                 :             :                     // Special case handling for the "!" operator (factorial)
     634         [ +  - ]:           3 :                     if (node.name[0] == '!') {
     635                 :           3 :                         m_evalNode(*node.childs[0], node.tmp[0]);
     636                 :           3 :                         vOutResult = m_Factorial(node.tmp[0]);  // Call the factorial function
     637                 :           3 :                     } else {
     638                 :           0 :                         throw ExprException(ErrorCode::FUNCTION_NOT_FOUND, "Function not found: " + node.name);
     639                 :           0 :                     }
     640                 :    16000146 :                 } else {
     641         [ +  + ]:    16000146 :                     if (it->second.argCount == 1) {
     642                 :    12000110 :                         m_evalNode(*node.childs[0], node.tmp[0]);
     643                 :    12000110 :                         vOutResult = it->second.unaryFunctor(node.tmp[0]);  // first
     644         [ +  + ]:    12000110 :                     } else if (it->second.argCount == 2) {
     645                 :     4000024 :                         m_evalNode(*node.childs[0], node.tmp[0]);  // first
     646                 :     4000024 :                         m_evalNode(*node.childs[1], node.tmp[1]);  // second
     647                 :     4000024 :                         vOutResult = it->second.binaryFunctor(node.tmp[0], node.tmp[1]);
     648         [ +  - ]:     4000024 :                     } else if (it->second.argCount == 3) {
     649                 :          12 :                         m_evalNode(*node.childs[0], node.tmp[0]);  // first
     650                 :          12 :                         m_evalNode(*node.childs[1], node.tmp[1]);  // second
     651                 :          12 :                         m_evalNode(*node.childs[2], node.tmp[2]);  // third
     652                 :          12 :                         vOutResult = it->second.ternaryFunctor(node.tmp[0], node.tmp[1], node.tmp[2]);
     653                 :          12 :                     }
     654                 :    16000146 :                 }
     655                 :    16000149 :             } break;
     656         [ -  + ]:    16000149 :             default: throw ExprException(ErrorCode::UNKNOWN_NODE_TYPE, "Unknown node type");
     657                 :   128000369 :         }
     658                 :             : 
     659                 :             :         // V�rification du r�sultat pour NaN ou Inf
     660         [ +  + ]:   128000355 :         if (::std::isnan(vOutResult)) {
     661                 :           2 :             throw ExprException(ErrorCode::EVALUATION_NAN, "Result is NaN");
     662         [ +  + ]:   128000353 :         } else if (::std::isinf(vOutResult)) {
     663                 :           2 :             throw ExprException(ErrorCode::EVALUATION_INF, "Result is Inf");
     664                 :           2 :         }
     665                 :             : 
     666         [ +  + ]:   128000351 :         if (m_Verbose) {
     667                 :           2 :             m_log("Evaluating Node: " + String::fromDouble(vOutResult) + "\n");
     668                 :           2 :         }
     669                 :   128000351 :     }
     670                 :             : 
     671                 :             :     // Parse an expression to create a syntax tree
     672                 :         490 :     Node m_parseExpression(::std::vector<Token>& tokens, size_t& pos, int precedence) {
     673         [ +  + ]:         490 :         if (pos >= tokens.size()) {
     674                 :           1 :             throw ExprException(ErrorCode::PARSE_ERROR, "Unexpected end of expression");
     675                 :           1 :         }
     676                 :         489 :         Node node = m_parseFactor(tokens, pos);
     677         [ +  + ]:         563 :         while (pos < tokens.size()) {
     678                 :         337 :             String op = tokens[pos].value;
     679                 :         337 :             int opPrecedence = 0;
     680         [ +  + ]:         337 :             if (tokens[pos].type == TokenType::OPERATOR) {
     681 [ +  + ][ -  + ]:          93 :                 if (op == "+" || op == "-" || op == "*" || op == "/" || op == "^" || op == "%" || op == "!") {
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
                 [ -  + ]
     682 [ +  + ][ -  + ]:          89 :                     opPrecedence = (op == "+" || op == "-") ? 1 : (op == "*" || op == "/" || op == "%") ? 2 : 3;
         [ +  + ][ +  + ]
                 [ +  + ]
     683                 :          89 :                 } else {
     684                 :           4 :                     throw ExprException(ErrorCode::OPERATOR_NOT_FOUND, "Operator not found: " + op);
     685                 :           4 :                 }
     686                 :          93 :             }
     687         [ +  + ]:         333 :             if (opPrecedence <= precedence) {
     688                 :         256 :                 break;
     689                 :         256 :             }
     690                 :          77 :             ++pos;
     691         [ +  + ]:          77 :             if (pos >= tokens.size()) {
     692                 :           3 :                 throw ExprException(ErrorCode::PARSE_ERROR, "Incomplete expression after operator: " + op);
     693                 :           3 :             }
     694                 :          74 :             Node rightNode = m_parseExpression(tokens, pos, opPrecedence);
     695                 :          74 :             Node opNode;
     696                 :          74 :             opNode.type = NodeType::OPERATOR;
     697                 :          74 :             opNode.name = op;
     698                 :          74 :             opNode.childCount = 2;
     699                 :          74 :             opNode.childs[0] = ::std::make_shared<Node>(node);
     700                 :          74 :             opNode.childs[1] = ::std::make_shared<Node>(rightNode);
     701                 :          74 :             node = opNode;
     702                 :          74 :         }
     703                 :         482 :         return node;
     704                 :         489 :     }
     705                 :             : 
     706                 :             :     // Parse a factor in an expression (number, variable, parenthesis, function)
     707                 :         519 :     Node m_parseFactor(::std::vector<Token>& tokens, size_t& pos) {
     708                 :         519 :         Node node;
     709         [ -  + ]:         519 :         if (pos >= tokens.size()) {
     710                 :           0 :             throw ExprException(ErrorCode::PARSE_ERROR, "Unexpected end of expression");
     711                 :           0 :         }
     712                 :             : 
     713         [ +  + ]:         519 :         if (tokens[pos].type == TokenType::NUMBER) {
     714                 :         258 :             node.type = NodeType::NUMBER;
     715                 :         258 :             node.value = ::std::stod(tokens[pos].value.c_str());
     716                 :         258 :             ++pos;
     717                 :             : 
     718                 :             :             // Gestion de l'op�rateur postfix� "!" pour les nombres
     719 [ +  + ][ +  + ]:         258 :             if (pos < tokens.size() && tokens[pos].type == TokenType::OPERATOR && tokens[pos].value == "!") {
                 [ +  + ]
     720                 :           5 :                 Node opNode;
     721                 :           5 :                 opNode.type = NodeType::FUNCTION;
     722                 :           5 :                 opNode.name = "!";
     723                 :           5 :                 opNode.childs[0] = ::std::make_shared<Node>(node);
     724                 :           5 :                 opNode.childCount = 1;
     725                 :           5 :                 node = opNode;  // Remplacer le n�ud par l'op�rateur postfix�
     726                 :           5 :                 ++pos;
     727                 :           5 :             }
     728         [ +  + ]:         261 :         } else if (tokens[pos].type == TokenType::VARIABLE) {
     729                 :         199 :             String identifier = tokens[pos].value;
     730                 :         199 :             ++pos;
     731                 :             : 
     732 [ +  + ][ +  + ]:         199 :             if (pos < tokens.size() && tokens[pos].type == TokenType::LPAREN) {
     733                 :         143 :                 auto it = m_Functions.find(identifier);
     734         [ +  + ]:         143 :                 if (it == m_Functions.end()) {
     735                 :           3 :                     throw ExprException(ErrorCode::FUNCTION_NOT_FOUND, "Function not found: " + identifier);
     736                 :           3 :                 }
     737                 :             : 
     738                 :         140 :                 ++pos;  // Passer la parenth�se ouvrante
     739                 :             : 
     740 [ +  - ][ +  + ]:         140 :                 if (pos < tokens.size() && tokens[pos].type == TokenType::RPAREN) {
     741                 :           1 :                     throw ExprException(ErrorCode::FUNCTION_WRONG_ARGUMENTS_COUNT, "Function called with incorrect number of arguments: " + identifier);
     742                 :           1 :                 }
     743                 :             : 
     744                 :         139 :                 node.type = NodeType::FUNCTION;
     745                 :         139 :                 node.name = identifier;
     746                 :         139 :                 node.childs[0] = ::std::make_shared<Node>(m_parseExpression(tokens, pos, 0));
     747                 :         139 :                 node.childCount = 1;
     748                 :             : 
     749 [ +  + ][ +  + ]:         195 :                 while (pos < tokens.size() && tokens[pos].type == TokenType::SEPARATOR) {
     750                 :          56 :                     ++pos;
     751         [ -  + ]:          56 :                     if (node.childCount >= 3) {
     752                 :           0 :                         throw ExprException(ErrorCode::PARSE_ERROR, "Too many arguments provided for function: " + identifier);
     753                 :           0 :                     }
     754                 :          56 :                     node.childs[node.childCount++] = ::std::make_shared<Node>(m_parseExpression(tokens, pos, 0));
     755                 :          56 :                 }
     756                 :             : 
     757 [ +  + ][ -  + ]:         139 :                 if (pos >= tokens.size() || tokens[pos].type != TokenType::RPAREN) {
     758                 :           0 :                     throw ExprException(ErrorCode::UNMATCHED_PARENTHESIS, "Unmatched parenthesis");
     759                 :           0 :                 }
     760                 :         139 :                 ++pos;
     761                 :             : 
     762         [ +  + ]:         139 :                 if (node.childCount != it->second.argCount) {
     763                 :           3 :                     throw ExprException(ErrorCode::FUNCTION_WRONG_ARGUMENTS_COUNT, "Incorrect number of arguments for function: " + identifier);
     764                 :           3 :                 }
     765                 :             : 
     766                 :         139 :             } else {
     767                 :             :                 // Gestion des variables (non-fonctions et non-constantes)
     768                 :          56 :                 auto it = m_Constant.find(identifier);
     769         [ +  + ]:          56 :                 if (it != m_Constant.end()) {
     770                 :          12 :                     node.type = NodeType::NUMBER;
     771                 :          12 :                     node.value = it->second;
     772                 :          44 :                 } else {
     773                 :          44 :                     node.type = NodeType::VARIABLE;
     774                 :          44 :                     node.name = identifier;
     775                 :          44 :                     m_ParsedVariables[identifier] = 0.0;  // Ajout de la variable trouv�e avec une valeur par d�faut de 0.0
     776                 :          44 :                 }
     777                 :             : 
     778                 :             :                 // Gestion de l'op�rateur postfix� "!" pour les variables
     779 [ +  + ][ +  + ]:          56 :                 if (pos < tokens.size() && tokens[pos].type == TokenType::OPERATOR && tokens[pos].value == "!") {
                 [ -  + ]
     780                 :           0 :                     Node opNode;
     781                 :           0 :                     opNode.type = NodeType::FUNCTION;
     782                 :           0 :                     opNode.name = "!";
     783                 :           0 :                     opNode.childs[0] = ::std::make_shared<Node>(node);
     784                 :           0 :                     opNode.childCount = 1;
     785                 :           0 :                     node = opNode;  // Remplacer le n�ud par l'op�rateur postfix�
     786                 :           0 :                     ++pos;
     787                 :           0 :                 }
     788                 :          56 :             }
     789         [ +  + ]:         199 :         } else if (tokens[pos].type == TokenType::LPAREN) {
     790                 :          20 :             ++pos;
     791 [ +  - ][ +  + ]:          20 :             if (pos < tokens.size() && tokens[pos].type == TokenType::RPAREN) {
     792                 :           1 :                 throw ExprException(ErrorCode::EMPTY_PARENTHESIS, "Empty parenthesis found");
     793                 :           1 :             }
     794                 :          19 :             node = m_parseExpression(tokens, pos, 0);
     795 [ +  + ][ -  + ]:          19 :             if (pos >= tokens.size() || tokens[pos].type != TokenType::RPAREN) {
     796                 :           3 :                 throw ExprException(ErrorCode::UNMATCHED_PARENTHESIS, "Unmatched parenthesis");
     797                 :           3 :             }
     798                 :          16 :             ++pos;
     799         [ +  + ]:          42 :         } else if (tokens[pos].type == TokenType::OPERATOR) {
     800                 :          38 :             String op = tokens[pos].value;
     801         [ +  + ]:          38 :             if (op == "-") {
     802                 :          30 :                 node.type = NodeType::FUNCTION;
     803                 :          30 :                 node.name = tokens[pos].value;
     804                 :          30 :                 ++pos;
     805                 :          30 :                 node.childs[0] = ::std::make_shared<Node>(m_parseFactor(tokens, pos));
     806                 :          30 :                 node.childCount = 1;
     807         [ +  + ]:          30 :             } else if (op == "!") {
     808                 :           2 :                 throw ExprException(ErrorCode::PARSE_ERROR, "Factorial operator '!' cannot be used alone or in prefix position.");
     809                 :           6 :             } else {
     810                 :           6 :                 throw ExprException(ErrorCode::PARSE_ERROR, "Unexpected operator: " + tokens[pos].value);
     811                 :           6 :             }
     812                 :          38 :         } else {
     813                 :           4 :             throw ExprException(ErrorCode::PARSE_ERROR, "Unexpected token: " + tokens[pos].value);
     814                 :           4 :         }
     815                 :             : 
     816                 :         496 :         return node;
     817                 :         519 :     }
     818                 :             : 
     819                 :             :     /////////////////////////////////////////
     820                 :             :     //// BUILTINS FUNCTIONS /////////////////
     821                 :             :     /////////////////////////////////////////
     822                 :             : 
     823                 :             :     // Calculate a factorial
     824                 :           3 :     double m_Factorial(double vValue) {
     825 [ -  + ][ -  + ]:           3 :         if (vValue < 0 || ::std::floor(vValue) != vValue) {
     826                 :           0 :             throw ExprException(ErrorCode::PARSE_ERROR, "Factorial is not defined for negative or non-integer values.");
     827                 :           0 :         }
     828                 :           3 :         double result = 1;
     829                 :           3 :         const auto count = static_cast<int>(vValue);
     830         [ +  + ]:          11 :         for (int i = 1; i <= count; ++i) {
     831                 :           8 :             result *= i;
     832                 :           8 :         }
     833                 :           3 :         return result;
     834                 :           3 :     }
     835                 :             : 
     836                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/min.xhtml
     837                 :          12 :     double m_Min(double vX, double vY) {
     838         [ +  + ]:          12 :         if (vX < vY) {
     839                 :           8 :             return vX;
     840                 :           8 :         }
     841                 :           4 :         return vY;
     842                 :          12 :     }
     843                 :             : 
     844                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/max.xhtml
     845                 :          12 :     double m_Max(double vX, double vY) {
     846         [ +  + ]:          12 :         if (vX > vY) {
     847                 :           9 :             return vX;
     848                 :           9 :         }
     849                 :           3 :         return vY;
     850                 :          12 :     }
     851                 :             : 
     852                 :             :     // https://www.shadertoy.com/???? : sqrt(v*v+k) with k >= 0
     853                 :           3 :     double m_SmoothAbs(double vV, double vK) { return ::std::sqrt(vV * vV + ::std::abs(vK)); }
     854                 :             : 
     855                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/clamp.xhtml
     856                 :             :     // Clamp (glsl), Saturate (hlsl)
     857                 :           9 :     double m_Clamp(double vX, double vMinVal, double vMaxVal) { return m_Min(m_Max(vX, vMinVal), vMaxVal); }
     858                 :             : 
     859                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/smoothstep.xhtml
     860                 :           3 :     double m_SmoothStep(double vEdge0, double vEdge1, double vX) {
     861                 :           3 :         double t = m_Clamp((vX - vEdge0) / (vEdge1 - vEdge0), 0.0, 1.0);
     862                 :           3 :         return t * t * (3.0 - 2.0 * t);
     863                 :           3 :     }
     864                 :             : 
     865                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml
     866                 :             :     // Mix (glsl), lerp (hlsl)
     867                 :           6 :     double m_Mix(double vX, double vY, double vA) { return vX * (1.0 - vA) + vY * vA; }
     868                 :             : 
     869                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/step.xhtml
     870         [ +  + ]:           3 :     double m_Step(double vEdge, double vX) { return vX < vEdge ? 0.0 : 1.0; }
     871                 :             : 
     872                 :             :     // https://registry.khronos.org/OpenGL-Refpages/gl4/html/sign.xhtml
     873                 :           3 :     double m_Sign(double vX) {
     874         [ +  + ]:           3 :         if (vX < 0.0) {
     875                 :           1 :             return -1.0;
     876         [ +  + ]:           2 :         } else if (vX > 0.0) {
     877                 :           1 :             return 1.0;
     878                 :           1 :         }
     879                 :           1 :         return 0.0;
     880                 :           3 :     }
     881                 :             : };
     882                 :             : 
     883                 :             : }  // namespace ez
        

Generated by: LCOV version 2.0-1