From c90ef343c1f6c8c1ed6b9bb65a6786562364800b Mon Sep 17 00:00:00 2001
From: WerWolv <werwolv98@gmail.com>
Date: Sat, 28 Nov 2020 21:55:52 +0100
Subject: [PATCH] Added math evaluator / calculator to tools window

---
 CMakeLists.txt                           |   8 +-
 include/{ => helpers}/crypto.hpp         |   0
 include/{ => helpers}/event.hpp          |   0
 include/helpers/math_evaluator.hpp       |  88 +++++
 include/{ => helpers}/patches.hpp        |   0
 include/{ => helpers}/utils.hpp          |   0
 include/lang/pattern_data.hpp            |   2 +-
 include/views/view.hpp                   |   2 +-
 include/views/view_bookmarks.hpp         |   2 +-
 include/views/view_hexeditor.hpp         |   2 +-
 include/views/view_tools.hpp             |   7 +
 libs/ImGui/include/imgui_memory_editor.h |   2 +-
 source/{ => helpers}/crypto.cpp          |   2 +-
 source/helpers/math_evaluator.cpp        | 390 +++++++++++++++++++++++
 source/{ => helpers}/patches.cpp         |   4 +-
 source/{ => helpers}/utils.cpp           |   2 +-
 source/lang/parser.cpp                   |   2 +-
 source/lang/validator.cpp                |   2 +-
 source/providers/file_provider.cpp       |   2 +-
 source/views/view_data_inspector.cpp     |   2 +-
 source/views/view_disassembler.cpp       |   2 +-
 source/views/view_hashes.cpp             |   4 +-
 source/views/view_hexeditor.cpp          |   4 +-
 source/views/view_information.cpp        |   2 +-
 source/views/view_pattern.cpp            |   2 +-
 source/views/view_strings.cpp            |   2 +-
 source/views/view_tools.cpp              | 138 +++++++-
 27 files changed, 647 insertions(+), 26 deletions(-)
 rename include/{ => helpers}/crypto.hpp (100%)
 rename include/{ => helpers}/event.hpp (100%)
 create mode 100644 include/helpers/math_evaluator.hpp
 rename include/{ => helpers}/patches.hpp (100%)
 rename include/{ => helpers}/utils.hpp (100%)
 rename source/{ => helpers}/crypto.cpp (99%)
 create mode 100644 source/helpers/math_evaluator.cpp
 rename source/{ => helpers}/patches.cpp (98%)
 rename source/{ => helpers}/utils.cpp (98%)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ff907973..c0dfbdbc6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,9 +27,11 @@ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
 add_executable(ImHex
         source/main.cpp
         source/window.cpp
-        source/utils.cpp
-        source/crypto.cpp
-        source/patches.cpp
+
+        source/helpers/utils.cpp
+        source/helpers/crypto.cpp
+        source/helpers/patches.cpp
+        source/helpers/math_evaluator.cpp
 
         source/lang/preprocessor.cpp
         source/lang/lexer.cpp
diff --git a/include/crypto.hpp b/include/helpers/crypto.hpp
similarity index 100%
rename from include/crypto.hpp
rename to include/helpers/crypto.hpp
diff --git a/include/event.hpp b/include/helpers/event.hpp
similarity index 100%
rename from include/event.hpp
rename to include/helpers/event.hpp
diff --git a/include/helpers/math_evaluator.hpp b/include/helpers/math_evaluator.hpp
new file mode 100644
index 000000000..eb7c081e5
--- /dev/null
+++ b/include/helpers/math_evaluator.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <hex.hpp>
+
+#include <string>
+#include <vector>
+#include <queue>
+#include <unordered_map>
+#include <functional>
+#include <optional>
+
+namespace hex {
+
+    enum class TokenType {
+        Number,
+        Variable,
+        Function,
+        Operator,
+        Bracket
+    };
+
+    enum class Operator : u16 {
+        Invalid                 = 0x000,
+        Assign                  = 0x010,
+        Or                      = 0x020,
+        Xor                     = 0x030,
+        And                     = 0x040,
+        BitwiseOr               = 0x050,
+        BitwiseXor              = 0x060,
+        BitwiseAnd              = 0x070,
+        Equals                  = 0x080,
+        NotEquals               = 0x081,
+        GreaterThan             = 0x090,
+        LessThan                = 0x091,
+        GreaterThanOrEquals     = 0x092,
+        LessThanOrEquals        = 0x093,
+        ShiftLeft               = 0x0A0,
+        ShiftRight              = 0x0A1,
+        Addition                = 0x0B0,
+        Subtraction             = 0x0B1,
+        Multiplication          = 0x0C0,
+        Division                = 0x0C1,
+        Modulus                 = 0x0C2,
+        Exponentiation          = 0x1D0,
+        Combine                 = 0x0E0,
+        BitwiseNot              = 0x0F0,
+        Not                     = 0x0F1
+    };
+
+    enum class BracketType : std::uint8_t {
+        Left,
+        Right
+    };
+
+    struct Token {
+        TokenType type;
+
+        long double number;
+        Operator op;
+        BracketType bracketType;
+        std::string name;
+        std::vector<long double> arguments;
+    };
+
+    class MathEvaluator {
+    public:
+        MathEvaluator() = default;
+
+        std::optional<long double> evaluate(std::string input);
+
+        void registerStandardVariables();
+        void registerStandardFunctions();
+
+        void setVariable(std::string name, long double value);
+        void setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs);
+
+        const std::unordered_map<std::string, long double>& getVariables() { return this->m_variables; }
+
+    private:
+        std::queue<Token> parseInput(const char *input);
+        std::queue<Token> toPostfix(std::queue<Token> inputQueue);
+        std::optional<long double> evaluate(std::queue<Token> postfixTokens);
+
+        std::unordered_map<std::string, long double> m_variables;
+        std::unordered_map<std::string, std::function<std::optional<long double>(std::vector<long double>)>> m_functions;
+    };
+
+}
\ No newline at end of file
diff --git a/include/patches.hpp b/include/helpers/patches.hpp
similarity index 100%
rename from include/patches.hpp
rename to include/helpers/patches.hpp
diff --git a/include/utils.hpp b/include/helpers/utils.hpp
similarity index 100%
rename from include/utils.hpp
rename to include/helpers/utils.hpp
diff --git a/include/lang/pattern_data.hpp b/include/lang/pattern_data.hpp
index 9fa0bd02a..cf74a65e2 100644
--- a/include/lang/pattern_data.hpp
+++ b/include/lang/pattern_data.hpp
@@ -7,7 +7,7 @@
 #include "imgui_memory_editor.h"
 
 #include "providers/provider.hpp"
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <random>
 
diff --git a/include/views/view.hpp b/include/views/view.hpp
index 2b9892e48..d41d1446c 100644
--- a/include/views/view.hpp
+++ b/include/views/view.hpp
@@ -4,7 +4,7 @@
 
 #include "imgui.h"
 
-#include "event.hpp"
+#include "helpers/event.hpp"
 
 #include <functional>
 #include <string>
diff --git a/include/views/view_bookmarks.hpp b/include/views/view_bookmarks.hpp
index dba11a356..c1ac6864b 100644
--- a/include/views/view_bookmarks.hpp
+++ b/include/views/view_bookmarks.hpp
@@ -5,7 +5,7 @@
 #include <vector>
 #include <list>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 namespace hex {
 
diff --git a/include/views/view_hexeditor.hpp b/include/views/view_hexeditor.hpp
index 2f034e6e2..a0226bceb 100644
--- a/include/views/view_hexeditor.hpp
+++ b/include/views/view_hexeditor.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 #include "views/view.hpp"
 
 #include "imgui_memory_editor.h"
diff --git a/include/views/view_tools.hpp b/include/views/view_tools.hpp
index 22f888ad4..09468ba40 100644
--- a/include/views/view_tools.hpp
+++ b/include/views/view_tools.hpp
@@ -4,6 +4,7 @@
 
 #include "imgui.h"
 #include "views/view.hpp"
+#include "helpers/math_evaluator.hpp"
 
 #include <array>
 #include <string>
@@ -33,10 +34,16 @@ namespace hex {
 
         std::array<float, 4> m_pickedColor;
 
+        MathEvaluator m_mathEvaluator;
+        std::vector<long double> m_mathHistory;
+        std::string m_lastMathError;
+        char *m_mathInput = nullptr;
+
         void drawDemangler();
         void drawASCIITable();
         void drawRegexReplacer();
         void drawColorPicker();
+        void drawMathEvaluator();
     };
 
 }
\ No newline at end of file
diff --git a/libs/ImGui/include/imgui_memory_editor.h b/libs/ImGui/include/imgui_memory_editor.h
index cff430f35..809bfb2e2 100644
--- a/libs/ImGui/include/imgui_memory_editor.h
+++ b/libs/ImGui/include/imgui_memory_editor.h
@@ -46,7 +46,7 @@
 
 #include <stdio.h>      // sprintf, scanf
 #include <stdint.h>     // uint8_t, etc.
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include "views/view.hpp"
 
diff --git a/source/crypto.cpp b/source/helpers/crypto.cpp
similarity index 99%
rename from source/crypto.cpp
rename to source/helpers/crypto.cpp
index f854f251a..fa234287d 100644
--- a/source/crypto.cpp
+++ b/source/helpers/crypto.cpp
@@ -1,4 +1,4 @@
-#include "crypto.hpp"
+#include "helpers/crypto.hpp"
 
 #include "providers/provider.hpp"
 
diff --git a/source/helpers/math_evaluator.cpp b/source/helpers/math_evaluator.cpp
new file mode 100644
index 000000000..df7776033
--- /dev/null
+++ b/source/helpers/math_evaluator.cpp
@@ -0,0 +1,390 @@
+#include "helpers/math_evaluator.hpp"
+
+#include <string>
+#include <queue>
+#include <stack>
+#include <stdexcept>
+#include <cmath>
+#include <cstdint>
+#include <optional>
+#include <numbers>
+
+namespace hex {
+
+    s16 comparePrecedence(const Operator& a, const Operator& b) {
+        return (static_cast<s8>(a) & 0x0F0) - (static_cast<s8>(b) & 0x0F0);
+    }
+
+    bool isLeftAssociative(const Operator op) {
+        return (static_cast<u32>(op) & 0xF00) == 0;
+    }
+
+    std::pair<Operator, size_t> toOperator(std::string input) {
+        if (input.starts_with("##"))  return { Operator::Combine,               2 };
+        if (input.starts_with("=="))  return { Operator::Equals,                2 };
+        if (input.starts_with("!="))  return { Operator::NotEquals,             2 };
+        if (input.starts_with(">="))  return { Operator::GreaterThanOrEquals,   2 };
+        if (input.starts_with("<="))  return { Operator::LessThanOrEquals,      2 };
+        if (input.starts_with(">>"))  return { Operator::ShiftRight,            2 };
+        if (input.starts_with("<<"))  return { Operator::ShiftLeft,             2 };
+        if (input.starts_with("||"))  return { Operator::Or,                    2 };
+        if (input.starts_with("^^"))  return { Operator::Xor,                   2 };
+        if (input.starts_with("&&"))  return { Operator::And,                   2 };
+        if (input.starts_with("**"))  return { Operator::Exponentiation,        2 };
+        if (input.starts_with(">"))   return { Operator::GreaterThan,           1 };
+        if (input.starts_with("<"))   return { Operator::LessThan,              1 };
+        if (input.starts_with("!"))   return { Operator::Not,                   1 };
+        if (input.starts_with("|"))   return { Operator::BitwiseOr,             1 };
+        if (input.starts_with("^"))   return { Operator::BitwiseXor,            1 };
+        if (input.starts_with("&"))   return { Operator::BitwiseAnd,            1 };
+        if (input.starts_with("~"))   return { Operator::BitwiseNot,            1 };
+        if (input.starts_with("+"))   return { Operator::Addition,              1 };
+        if (input.starts_with("-"))   return { Operator::Subtraction,           1 };
+        if (input.starts_with("*"))   return { Operator::Multiplication,        1 };
+        if (input.starts_with("/"))   return { Operator::Division,              1 };
+        if (input.starts_with("%"))   return { Operator::Modulus,               1 };
+        if (input.starts_with("="))   return { Operator::Assign,                1 };
+
+        return { Operator::Invalid, 0 };
+    }
+
+    std::queue<Token> MathEvaluator::parseInput(const char *input) {
+        std::queue<Token> inputQueue;
+
+        char *prevPos = const_cast<char*>(input);
+        for (char *pos = prevPos; *pos != 0x00;) {
+            if (std::isdigit(*pos) || *pos == '.') {
+                auto number = std::strtold(pos, &pos);
+
+                if (*pos == 'x') {
+                    pos--;
+                    number = std::strtoull(pos, &pos, 0);
+                }
+
+                inputQueue.push(Token{ .type = TokenType::Number, .number = number });
+            } else if (*pos == '(') {
+                inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Left});
+                pos++;
+            } else if (*pos == ')') {
+                inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Right});
+                pos++;
+            } else if (std::isspace(*pos)) {
+                pos++;
+            } else {
+                auto [op, width] = toOperator(pos);
+
+                if (op != Operator::Invalid) {
+                    inputQueue.push(Token{ .type = TokenType::Operator, .op = op });
+                    pos += width;
+                } else {
+                    Token token;
+
+                    while (std::isalpha(*pos) || *pos == '_') {
+                        token.name += *pos;
+                        pos++;
+                    }
+
+                    if (*pos == '(') {
+                        pos++;
+
+                        u32 depth = 1;
+                        std::vector<std::string> expressions;
+                        expressions.emplace_back();
+
+                        while (*pos != 0x00) {
+                            if (*pos == '(') depth++;
+                            else if (*pos == ')') depth--;
+
+                            if (depth == 0)
+                                break;
+
+                            if (depth == 1 && *pos == ',') {
+                                expressions.emplace_back();
+                                pos++;
+                            }
+
+                            expressions.back() += *pos;
+
+                            pos++;
+                        }
+
+                        pos++;
+
+                        for (const auto &expression : expressions) {
+                            if (expression == "" && expressions.size() > 1)
+                                throw std::invalid_argument("Invalid function call syntax!");
+                            else if (expression == "")
+                                break;
+
+                            auto inputQueue = parseInput(expression.c_str());
+                            auto postfixTokens = toPostfix(inputQueue);
+                            auto result = evaluate(postfixTokens);
+
+                            if (!result.has_value())
+                                throw std::invalid_argument("Invalid argument for function!");
+
+                            token.arguments.push_back(result.value());
+                        }
+
+                        token.type = TokenType::Function;
+                        inputQueue.push(token);
+
+                    } else {
+                        token.type = TokenType::Variable;
+                        inputQueue.push(token);
+                    }
+                }
+
+
+            }
+
+            if (prevPos == pos)
+                throw std::invalid_argument("Invalid syntax!");
+
+            prevPos = pos;
+        }
+
+        return inputQueue;
+    }
+
+    std::queue<Token> MathEvaluator::toPostfix(std::queue<Token> inputQueue) {
+        std::queue<Token> outputQueue;
+        std::stack<Token> operatorStack;
+
+        while (!inputQueue.empty()) {
+            Token currToken = inputQueue.front();
+            inputQueue.pop();
+
+            if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function)
+                outputQueue.push(currToken);
+            else if (currToken.type == TokenType::Operator) {
+                while ((!operatorStack.empty())
+                       && (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op)))
+                       && operatorStack.top().type != TokenType::Bracket) {
+                    outputQueue.push(operatorStack.top());
+                    operatorStack.pop();
+                }
+                operatorStack.push(currToken);
+            } else if (currToken.type == TokenType::Bracket) {
+                if (currToken.bracketType == BracketType::Left)
+                    operatorStack.push(currToken);
+                else {
+                    if (operatorStack.empty())
+                        throw std::invalid_argument("Mismatching parenthesis!");
+
+                    while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) {
+                        if (operatorStack.empty())
+                            throw std::invalid_argument("Mismatching parenthesis!");
+
+                        outputQueue.push(operatorStack.top());
+                        operatorStack.pop();
+                    }
+
+                    operatorStack.pop();
+                }
+            }
+        }
+
+        while (!operatorStack.empty()) {
+            auto top = operatorStack.top();
+
+            if (top.type == TokenType::Bracket)
+                throw std::invalid_argument("Mismatching parenthesis!");
+
+            outputQueue.push(top);
+            operatorStack.pop();
+        }
+
+        return outputQueue;
+    }
+
+    std::optional<long double> MathEvaluator::evaluate(std::queue<Token> postfixTokens) {
+        std::stack<long double> evaluationStack;
+
+        while (!postfixTokens.empty()) {
+            auto front = postfixTokens.front();
+            postfixTokens.pop();
+
+            if (front.type == TokenType::Number)
+                evaluationStack.push(front.number);
+            else if (front.type == TokenType::Operator) {
+                long double rightOperand, leftOperand;
+                if (evaluationStack.size() < 2) {
+                    if ((front.op == Operator::Addition || front.op == Operator::Subtraction || front.op == Operator::Not || front.op == Operator::BitwiseNot) && evaluationStack.size() == 1) {
+                        rightOperand = evaluationStack.top(); evaluationStack.pop();
+                        leftOperand = 0;
+                    }
+                    else throw std::invalid_argument("Not enough operands for operator!");
+                } else {
+                    rightOperand = evaluationStack.top(); evaluationStack.pop();
+                    leftOperand = evaluationStack.top(); evaluationStack.pop();
+                }
+
+                long double result = std::numeric_limits<long double>::quiet_NaN();
+                switch (front.op) {
+                    default:
+                    case Operator::Invalid:
+                        throw std::invalid_argument("Invalid operator!");
+                    case Operator::And:
+                        result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::Or:
+                        result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::Xor:
+                        result = (static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand)) > 0;
+                        break;
+                    case Operator::GreaterThan:
+                        result = leftOperand > rightOperand;
+                        break;
+                    case Operator::LessThan:
+                        result = leftOperand < rightOperand;
+                        break;
+                    case Operator::GreaterThanOrEquals:
+                        result = leftOperand >= rightOperand;
+                        break;
+                    case Operator::LessThanOrEquals:
+                        result = leftOperand <= rightOperand;
+                        break;
+                    case Operator::Equals:
+                        result = leftOperand == rightOperand;
+                        break;
+                    case Operator::NotEquals:
+                        result = leftOperand != rightOperand;
+                        break;
+                    case Operator::Not:
+                        result = !static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::BitwiseOr:
+                        result = static_cast<s64>(leftOperand) | static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::BitwiseXor:
+                        result = static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::BitwiseAnd:
+                        result = static_cast<s64>(leftOperand) & static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::BitwiseNot:
+                        result = ~static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::ShiftLeft:
+                        result = static_cast<s64>(leftOperand) << static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::ShiftRight:
+                        result = static_cast<s64>(leftOperand) >> static_cast<s64>(rightOperand);
+                        break;
+                    case Operator::Addition:
+                        result = leftOperand + rightOperand;
+                        break;
+                    case Operator::Subtraction:
+                        result = leftOperand - rightOperand;
+                        break;
+                    case Operator::Multiplication:
+                        result = leftOperand * rightOperand;
+                        break;
+                    case Operator::Division:
+                        result = leftOperand / rightOperand;
+                        break;
+                    case Operator::Modulus:
+                        result = std::fmod(leftOperand, rightOperand);
+                        break;
+                    case Operator::Exponentiation:
+                        result = std::pow(leftOperand, rightOperand);
+                        break;
+                    case Operator::Combine:
+                        result = (static_cast<u64>(leftOperand) << (64 - __builtin_clzll(static_cast<u64>(rightOperand)))) | static_cast<u64>(rightOperand);
+                        break;
+                }
+
+                evaluationStack.push(result);
+            } else if (front.type == TokenType::Variable) {
+                if (this->m_variables.contains(front.name))
+                    evaluationStack.push(this->m_variables.at(front.name));
+                else
+                    throw std::invalid_argument("Unknown variable!");
+            } else if (front.type == TokenType::Function) {
+                if (!this->m_functions[front.name])
+                    throw std::invalid_argument("Unknown function called!");
+
+                auto result = this->m_functions[front.name](front.arguments);
+
+                if (result.has_value())
+                    evaluationStack.push(result.value());
+            } else
+                throw std::invalid_argument("Parenthesis in postfix expression!");
+
+        }
+
+        if (evaluationStack.empty())
+            return { };
+        else if (evaluationStack.size() > 1)
+            throw std::invalid_argument("Undigested input left!");
+        else
+            return evaluationStack.top();
+    }
+
+
+    std::optional<long double> MathEvaluator::evaluate(std::string input) {
+        auto inputQueue = parseInput(input.c_str());
+
+        std::string resultVariable = "ans";
+
+        {
+            std::queue<Token> queueCopy = inputQueue;
+            if (queueCopy.front().type == TokenType::Variable) {
+                resultVariable = queueCopy.front().name;
+                queueCopy.pop();
+                if (queueCopy.front().type != TokenType::Operator || queueCopy.front().op != Operator::Assign)
+                    resultVariable = "ans";
+                else {
+                    inputQueue.pop();
+                    inputQueue.pop();
+                }
+            }
+        }
+
+        auto postfixTokens = toPostfix(inputQueue);
+
+        auto result = evaluate(postfixTokens);
+
+        if (result.has_value()) {
+            this->setVariable(resultVariable, result.value());
+        }
+
+        return result;
+    }
+
+    void MathEvaluator::setVariable(std::string name, long double value) {
+        this->m_variables[name] = value;
+    }
+
+    void MathEvaluator::setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs) {
+        this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) {
+            if (args.size() < minNumArgs || args.size() > maxNumArgs)
+                throw std::invalid_argument("Invalid number of function arguments!");
+
+            return function(args);
+        };
+    }
+
+
+    void MathEvaluator::registerStandardVariables() {
+        this->setVariable("ans", 0);
+    }
+
+    void MathEvaluator::registerStandardFunctions() {
+        this->setFunction("sin",   [](auto args){ return std::sin(args[0]); }, 1, 1);
+        this->setFunction("cos",   [](auto args){ return std::cos(args[0]); }, 1, 1);
+        this->setFunction("tan",   [](auto args){ return std::tan(args[0]); }, 1, 1);
+        this->setFunction("sqrt",  [](auto args){ return std::sqrt(args[0]); }, 1, 1);
+        this->setFunction("ceil",  [](auto args){ return std::ceil(args[0]); }, 1, 1);
+        this->setFunction("floor", [](auto args){ return std::floor(args[0]); }, 1, 1);
+        this->setFunction("sign",  [](auto args){ return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 : -1; }, 1, 1);
+        this->setFunction("abs",   [](auto args){ return std::abs(args[0]); }, 1, 1);
+        this->setFunction("ln",    [](auto args){ return std::log(args[0]); }, 1, 1);
+        this->setFunction("lb",    [](auto args){ return std::log2(args[0]); }, 1, 1);
+        this->setFunction("log",   [](auto args){ return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2);
+    }
+
+}
+
diff --git a/source/patches.cpp b/source/helpers/patches.cpp
similarity index 98%
rename from source/patches.cpp
rename to source/helpers/patches.cpp
index 708bc0a1d..258a1f97e 100644
--- a/source/patches.cpp
+++ b/source/helpers/patches.cpp
@@ -1,11 +1,11 @@
-#include "patches.hpp"
+#include "helpers/patches.hpp"
 
 #include <concepts>
 #include <cstring>
 #include <string_view>
 #include <type_traits>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 namespace hex {
 
diff --git a/source/utils.cpp b/source/helpers/utils.cpp
similarity index 98%
rename from source/utils.cpp
rename to source/helpers/utils.cpp
index 83468946b..7a0460176 100644
--- a/source/utils.cpp
+++ b/source/helpers/utils.cpp
@@ -1,4 +1,4 @@
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <codecvt>
 #include <locale>
diff --git a/source/lang/parser.cpp b/source/lang/parser.cpp
index 16e891975..a3e3562a8 100644
--- a/source/lang/parser.cpp
+++ b/source/lang/parser.cpp
@@ -1,6 +1,6 @@
 #include "lang/parser.hpp"
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <optional>
 
diff --git a/source/lang/validator.cpp b/source/lang/validator.cpp
index 06e5208eb..5763741af 100644
--- a/source/lang/validator.cpp
+++ b/source/lang/validator.cpp
@@ -3,7 +3,7 @@
 #include <unordered_set>
 #include <string>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 namespace hex::lang {
 
diff --git a/source/providers/file_provider.cpp b/source/providers/file_provider.cpp
index c58709a21..2e5839c9e 100644
--- a/source/providers/file_provider.cpp
+++ b/source/providers/file_provider.cpp
@@ -6,7 +6,7 @@
 #include <sys/stat.h>
 #include <time.h>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 namespace hex::prv {
 
diff --git a/source/views/view_data_inspector.cpp b/source/views/view_data_inspector.cpp
index 0c239b5cc..4f23f8be7 100644
--- a/source/views/view_data_inspector.cpp
+++ b/source/views/view_data_inspector.cpp
@@ -1,7 +1,7 @@
 #include "views/view_data_inspector.hpp"
 
 #include "providers/provider.hpp"
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <cstring>
 
diff --git a/source/views/view_disassembler.cpp b/source/views/view_disassembler.cpp
index 016176d1a..b329091d8 100644
--- a/source/views/view_disassembler.cpp
+++ b/source/views/view_disassembler.cpp
@@ -1,7 +1,7 @@
 #include "views/view_disassembler.hpp"
 
 #include "providers/provider.hpp"
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <cstring>
 
diff --git a/source/views/view_hashes.cpp b/source/views/view_hashes.cpp
index 6ecc25d22..763b09dc5 100644
--- a/source/views/view_hashes.cpp
+++ b/source/views/view_hashes.cpp
@@ -2,11 +2,11 @@
 
 #include "providers/provider.hpp"
 
-#include "crypto.hpp"
+#include "helpers/crypto.hpp"
 
 #include <vector>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 namespace hex {
 
diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp
index 294914b7e..d21559ff4 100644
--- a/source/views/view_hexeditor.cpp
+++ b/source/views/view_hexeditor.cpp
@@ -5,8 +5,8 @@
 
 #include <GLFW/glfw3.h>
 
-#include "crypto.hpp"
-#include "patches.hpp"
+#include "helpers/crypto.hpp"
+#include "helpers/patches.hpp"
 
 #undef __STRICT_ANSI__
 #include <cstdio>
diff --git a/source/views/view_information.cpp b/source/views/view_information.cpp
index 755f8d460..0386b2e76 100644
--- a/source/views/view_information.cpp
+++ b/source/views/view_information.cpp
@@ -2,7 +2,7 @@
 
 #include "providers/provider.hpp"
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <cstring>
 #include <cmath>
diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp
index c4fa0b660..acf73b612 100644
--- a/source/views/view_pattern.cpp
+++ b/source/views/view_pattern.cpp
@@ -5,7 +5,7 @@
 #include "lang/lexer.hpp"
 #include "lang/validator.hpp"
 #include "lang/evaluator.hpp"
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <magic.h>
 
diff --git a/source/views/view_strings.cpp b/source/views/view_strings.cpp
index 7dbddc505..0e09cfc09 100644
--- a/source/views/view_strings.cpp
+++ b/source/views/view_strings.cpp
@@ -1,7 +1,7 @@
 #include "views/view_strings.hpp"
 
 #include "providers/provider.hpp"
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <cstring>
 
diff --git a/source/views/view_tools.cpp b/source/views/view_tools.cpp
index 9db06836d..150d0a00b 100644
--- a/source/views/view_tools.cpp
+++ b/source/views/view_tools.cpp
@@ -2,8 +2,9 @@
 
 #include <cstring>
 #include <regex>
+#include <optional>
 
-#include "utils.hpp"
+#include "helpers/utils.hpp"
 
 #include <llvm/Demangle/Demangle.h>
 
@@ -11,7 +12,6 @@ namespace hex {
 
     ViewTools::ViewTools() : View("Tools") {
         this->m_mangledBuffer = new char[0xF'FFFF];
-
         std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
 
         this->m_regexInput = new char[0xF'FFFF];
@@ -20,6 +20,19 @@ namespace hex {
         std::memset(this->m_regexInput, 0x00, 0xF'FFFF);
         std::memset(this->m_regexPattern, 0x00, 0xF'FFFF);
         std::memset(this->m_replacePattern, 0x00, 0xF'FFFF);
+
+
+        this->m_mathInput = new char[0xFFFF];
+        std::memset(this->m_mathInput, 0x00, 0xFFFF);
+        this->m_mathEvaluator.registerStandardVariables();
+        this->m_mathEvaluator.registerStandardFunctions();
+        this->m_mathEvaluator.setFunction("clear", [this](auto args) -> std::optional<long double> {
+            this->m_mathHistory.clear();
+            this->m_lastMathError.clear();
+            std::memset(this->m_mathInput, 0x00, 0xFFFF);
+
+            return { };
+        }, 0, 0);
     }
 
     ViewTools::~ViewTools() {
@@ -28,6 +41,8 @@ namespace hex {
         delete[] this->m_regexInput;
         delete[] this->m_regexPattern;
         delete[] this->m_replacePattern;
+
+        delete[] this->m_mathInput;
     }
 
     void ViewTools::drawDemangler() {
@@ -124,12 +139,131 @@ namespace hex {
         }
     }
 
+    void ViewTools::drawMathEvaluator() {
+        if (ImGui::CollapsingHeader("Calculator")) {
+            if (ImGui::InputText("Input", this->m_mathInput, 0xFFFF, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
+                ImGui::SetKeyboardFocusHere();
+                std::optional<long double> result;
+
+                try {
+                    result = this->m_mathEvaluator.evaluate(this->m_mathInput);
+                } catch (std::invalid_argument &e) {
+                    this->m_lastMathError = e.what();
+                }
+
+                if (result.has_value()) {
+                    this->m_mathHistory.push_back(result.value());
+                    std::memset(this->m_mathInput, 0x00, 0xFFFF);
+                    this->m_lastMathError.clear();
+                }
+
+            }
+
+            if (!this->m_lastMathError.empty())
+                ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", this->m_lastMathError.c_str());
+            else
+                ImGui::NewLine();
+
+            enum class MathDisplayType { Standard, Scientific, Programmer } mathDisplayType;
+            if (ImGui::BeginTabBar("##mathFormatTabBar")) {
+                if (ImGui::BeginTabItem("Standard")) {
+                    mathDisplayType = MathDisplayType::Standard;
+                    ImGui::EndTabItem();
+                }
+                if (ImGui::BeginTabItem("Scientific")) {
+                    mathDisplayType = MathDisplayType::Scientific;
+                    ImGui::EndTabItem();
+                }
+                if (ImGui::BeginTabItem("Programmer")) {
+                    mathDisplayType = MathDisplayType::Programmer;
+                    ImGui::EndTabItem();
+                }
+
+                ImGui::EndTabBar();
+            }
+
+            if (ImGui::BeginTable("##mathWrapper", 2)) {
+                ImGui::TableSetupColumn("##results");
+                ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
+
+                ImGui::TableNextRow();
+                ImGui::TableNextColumn();
+                if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
+                    ImGui::TableSetupScrollFreeze(0, 1);
+                    ImGui::TableSetupColumn("History");
+
+                    ImGuiListClipper clipper;
+                    clipper.Begin(this->m_mathHistory.size());
+
+                    ImGui::TableHeadersRow();
+                    while (clipper.Step()) {
+                        for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
+                            ImGui::TableNextRow();
+                            ImGui::TableNextColumn();
+
+                            switch (mathDisplayType) {
+                                case MathDisplayType::Standard:
+                                    ImGui::Text("%.3Lf", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
+                                    break;
+                                case MathDisplayType::Scientific:
+                                    ImGui::Text("%.6Le", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]);
+                                    break;
+                                case MathDisplayType::Programmer:
+                                    ImGui::Text("0x%llX (%llu)",
+                                                u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]),
+                                                u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]));
+                                    break;
+                            }
+                        }
+                    }
+
+                    clipper.End();
+
+                    ImGui::EndTable();
+                }
+
+                ImGui::TableNextColumn();
+                if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
+                    ImGui::TableSetupScrollFreeze(0, 1);
+                    ImGui::TableSetupColumn("Name");
+                    ImGui::TableSetupColumn("Value");
+
+                    ImGui::TableHeadersRow();
+                    for (const auto &[name, value] : this->m_mathEvaluator.getVariables()) {
+                        ImGui::TableNextRow();
+                        ImGui::TableNextColumn();
+                        ImGui::TextUnformatted(name.c_str());
+
+                        ImGui::TableNextColumn();
+                        switch (mathDisplayType) {
+                            case MathDisplayType::Standard:
+                                ImGui::Text("%.3Lf", value);
+                                break;
+                            case MathDisplayType::Scientific:
+                                ImGui::Text("%.6Le", value);
+                                break;
+                            case MathDisplayType::Programmer:
+                                ImGui::Text("0x%llX (%llu)", u64(value),  u64(value));
+                                break;
+                        }
+                    }
+
+                    ImGui::EndTable();
+                }
+
+                ImGui::EndTable();
+            }
+
+        }
+    }
+
     void ViewTools::createView() {
         if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
 
             this->drawDemangler();
             this->drawASCIITable();
             this->drawRegexReplacer();
+            this->drawMathEvaluator();
             this->drawColorPicker();
 
         }