From 08c21e6efeb94d2d9ca306955fdd9b82eec3660a Mon Sep 17 00:00:00 2001 From: whaffman Date: Mon, 8 Sep 2025 16:09:46 +0200 Subject: [PATCH] Implement RPN class with evaluation logic, exception handling, and main function; update PmergeMe class to track comparisons and enhance input validation --- .gitignore | 3 +- .vscode/settings.json | 4 +- ex01/Makefile | 2 + ex01/inc/RPN.hpp | 48 ++++++++++++ ex01/src/RPN.cpp | 172 ++++++++++++++++++++++++++++++++++++++++++ ex01/src/main.cpp | 25 ++++++ ex02/inc/PmergeMe.hpp | 1 + ex02/inc/PmergeMe.tpp | 44 +++++++---- ex02/src/PmergeMe.cpp | 26 ------- ex02/src/main.cpp | 18 ++++- 10 files changed, 299 insertions(+), 44 deletions(-) create mode 100644 ex01/Makefile create mode 100644 ex01/inc/RPN.hpp create mode 100644 ex01/src/RPN.cpp create mode 100644 ex01/src/main.cpp delete mode 100644 ex02/src/PmergeMe.cpp diff --git a/.gitignore b/.gitignore index 10e3026..2a8f625 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache *.d *.o -PmergeMe \ No newline at end of file +PmergeMe +RPN \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 72e45a9..e14407e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,6 +52,8 @@ "iomanip": "cpp", "sstream": "cpp", "*.tpp": "cpp", - "chrono": "cpp" + "chrono": "cpp", + "list": "cpp", + "ratio": "cpp" } } \ No newline at end of file diff --git a/ex01/Makefile b/ex01/Makefile new file mode 100644 index 0000000..57c02d6 --- /dev/null +++ b/ex01/Makefile @@ -0,0 +1,2 @@ +NAME := RPN +include ../common.mk \ No newline at end of file diff --git a/ex01/inc/RPN.hpp b/ex01/inc/RPN.hpp new file mode 100644 index 0000000..fc7c347 --- /dev/null +++ b/ex01/inc/RPN.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +class RPN +{ +public: + RPN(); + RPN(std::string expression); + RPN(const RPN &other); + ~RPN(); + RPN &operator=(const RPN &other); + void setExpression(std::string expression); + int evaluate(); + + class NoExpressionException : public std::exception + { + public: + const char *what() const noexcept override; + }; + + class InvalidExpressionException : public std::exception + { + public: + const char *what() const noexcept override; + }; + + class DivisionByZeroException : public std::exception + { + public: + const char *what() const noexcept override; + }; + + class InvalidCharacterException : public std::exception + { + public: + const char *what() const noexcept override; + }; + +private: + std::string _expression; + + bool isWellformed() const; + bool isOperator(char c) const; + int applyOperator(int a, int b, char op) const; + void printStack(std::stack stack) const; +}; diff --git a/ex01/src/RPN.cpp b/ex01/src/RPN.cpp new file mode 100644 index 0000000..e04c684 --- /dev/null +++ b/ex01/src/RPN.cpp @@ -0,0 +1,172 @@ +#include "RPN.hpp" + +#include +#include +#include +#include + +RPN::RPN() : _expression("") +{ +} + +RPN::RPN(std::string expression) : _expression(expression) +{ +} + +RPN::RPN(const RPN &other) : _expression(other._expression) +{ +} + +RPN::~RPN() +{ +} + +RPN &RPN::operator=(const RPN &other) +{ + if (this != &other) + { + _expression = other._expression; + } + return *this; +} + +void RPN::setExpression(std::string expression) +{ + _expression = expression; +} + +int RPN::evaluate() +{ + if (_expression.empty()) + throw RPN::NoExpressionException(); + if (!isWellformed()) + throw RPN::InvalidExpressionException(); + std::stack stack; + for (char c : _expression) + { + if (std::isspace(c)) + continue; + if (std::isdigit(c)) + stack.push(c - '0'); + else if (isOperator(c)) + { + if (stack.size() < 2) + throw RPN::InvalidExpressionException(); + + int b = stack.top(); + stack.pop(); + int a = stack.top(); + stack.pop(); + int result = applyOperator(a, b, c); + stack.push(result); + } + else + { + throw RPN::InvalidCharacterException(); + } + // std::cout << "Stack after processing '" << c << "': "; + // printStack(stack); + } + if (stack.size() != 1) + throw RPN::InvalidExpressionException(); + return stack.top(); +} + +bool RPN::isWellformed() const +{ + bool needs_space = false; + for (char c : _expression) + { + if (std::isspace(c)) + { + if (needs_space) + needs_space = false; + continue; + } + if (std::isdigit(c) || isOperator(c)) + { + if (needs_space) + return false; + needs_space = true; + } + else + { + return false; + } + } + return true; +} + +bool RPN::isOperator(char c) const +{ + if (std::string("+-*/").find(c) != std::string::npos) + return true; + return false; +} + +int RPN::applyOperator(int a, int b, char op) const +{ + switch (op) + { + case '+': + return a + b; + case '-': + return a - b; + case '*': + return a * b; + case '/': + if (b == 0) + throw RPN::DivisionByZeroException(); + return a / b; + default: + throw RPN::InvalidExpressionException(); + + return 0; + } +} + +void RPN::printStack(std::stack stack) const +{ + if (stack.empty()) + { + std::cout << "[]\n"; + return; + } + + std::stack temp; + while (!stack.empty()) + { + temp.push(stack.top()); + stack.pop(); + } + + std::cout << "["; + while (!temp.empty()) + { + std::cout << temp.top(); + temp.pop(); + if (!temp.empty()) + std::cout << ", "; + } + std::cout << "]\n"; +} + +const char *RPN::NoExpressionException::NoExpressionException::what() const noexcept +{ + return "No expression provided."; +} + +const char *RPN::InvalidExpressionException::InvalidExpressionException::what() const noexcept +{ + return "Invalid RPN expression."; +} + +const char *RPN::DivisionByZeroException::DivisionByZeroException::what() const noexcept +{ + return "Division by zero."; +} + +const char *RPN::InvalidCharacterException::InvalidCharacterException::what() const noexcept +{ + return "Invalid character in expression."; +} \ No newline at end of file diff --git a/ex01/src/main.cpp b/ex01/src/main.cpp new file mode 100644 index 0000000..f9abf24 --- /dev/null +++ b/ex01/src/main.cpp @@ -0,0 +1,25 @@ +#include "RPN.hpp" +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + { + std::cerr << "Usage: " << argv[0] << " " << std::endl; + return 1; + } + + try + { + RPN rpn(argv[1]); + int result = rpn.evaluate(); + std::cout << result << std::endl; + } + catch (const std::exception &e) + { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/ex02/inc/PmergeMe.hpp b/ex02/inc/PmergeMe.hpp index b96141c..f02c998 100644 --- a/ex02/inc/PmergeMe.hpp +++ b/ex02/inc/PmergeMe.hpp @@ -18,6 +18,7 @@ public: void sort(); std::string getString() const; + int getComparisons() const; private: Container _data; diff --git a/ex02/inc/PmergeMe.tpp b/ex02/inc/PmergeMe.tpp index d044737..0c53db4 100644 --- a/ex02/inc/PmergeMe.tpp +++ b/ex02/inc/PmergeMe.tpp @@ -13,12 +13,14 @@ #include #include - -template -std::string getTypeName() { - if (std::is_same_v>) return "std::vector "; - if (std::is_same_v>) return "std::deque "; - return typeid(T).name(); // fallback to mangled name +template +std::string getTypeName() +{ + if (std::is_same_v>) + return "std::vector "; + if (std::is_same_v>) + return "std::deque "; + return typeid(T).name(); // fallback to mangled name } template @@ -27,13 +29,24 @@ PmergeMe::PmergeMe(const int argc, const char **argv) : _comparisons( for (int i = 1; argv[i] != nullptr; ++i) { size_t pos; - int value = std::stoi(argv[i], &pos); - if (value < 0) - throw std::invalid_argument("All inputs must be non-negative integers"); - if (pos != std::strlen(argv[i])) - throw std::invalid_argument("Invalid input: not a valid integer"); + try + { + int value = std::stoi(argv[i], &pos); + if (value < 0) + throw std::invalid_argument("All inputs must be non-negative integers"); + if (pos != std::strlen(argv[i])) + throw std::invalid_argument("Invalid input: not a valid integer"); - _data.push_back(value); + _data.push_back(value); + } + catch (const std::out_of_range &e) + { + throw std::out_of_range("Input value out of range"); + } + catch (const std::invalid_argument &e) + { + throw std::invalid_argument("Invalid input: not a valid integer"); + } } if (!areAllUnique()) @@ -93,6 +106,12 @@ std::string PmergeMe::getString() const return result; } +template +int PmergeMe::getComparisons() const +{ + return _comparisons; +} + template bool PmergeMe::less(int a, int b) { @@ -198,4 +217,3 @@ std::ostream &operator<<(std::ostream &os, const PmergeMe &obj) return os; } - diff --git a/ex02/src/PmergeMe.cpp b/ex02/src/PmergeMe.cpp deleted file mode 100644 index fce1e04..0000000 --- a/ex02/src/PmergeMe.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "PmergeMe.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -std::ostream &operator<<(std::ostream &os, const std::vector &obj) -{ - for (std::vector::const_iterator it = obj.begin(); it != obj.end(); ++it) - { - if (it != obj.begin()) - os << " "; - os << *it; - } - - return os; -} - - - diff --git a/ex02/src/main.cpp b/ex02/src/main.cpp index d8bdfd3..ca15918 100644 --- a/ex02/src/main.cpp +++ b/ex02/src/main.cpp @@ -10,12 +10,14 @@ int main(const int argc, const char **argv) try { - PmergeMe > sorter(argc, argv); - std::cout << "Before: " << sorter << std::endl; - sorter.sort(); + PmergeMe > sorter_vector(argc, argv); + std::cout << "Before: " << sorter_vector << std::endl; + sorter_vector.sort(); PmergeMe > sorter_deque(argc, argv); sorter_deque.sort(); std::cout << "After: " << sorter_deque << std::endl; + std::cout << "Total comparisons (vector): " << sorter_vector.getComparisons() << std::endl; + std::cout << "Total comparisons (deque): " << sorter_deque.getComparisons() << std::endl; } catch (const std::invalid_argument &e) @@ -23,6 +25,16 @@ int main(const int argc, const char **argv) std::cerr << "Error: " << e.what() << std::endl; return 1; } + catch (const std::out_of_range &e) + { + std::cerr << "Error: "<< e.what() << std::endl; + return 1; + } + catch (const std::exception &e) + { + std::cerr << "Unexpected error: " << e.what() << std::endl; + return 1; + } return 0; }