From 05ad201fd3c2c0acfe8eb142e2bb2f8336f5d5a0 Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Sun, 6 Apr 2025 14:28:40 +0300 Subject: [PATCH 1/4] add task 12-14 --- CMakeLists.txt | 2 +- week-15/CMakeLists.txt | 4 ++ week-15/task-12-14/CMakeLists.txt | 3 + week-15/task-12-14/include/car_numbers.hpp | 16 +++++ week-15/task-12-14/test/CMakeLists.txt | 1 + week-15/task-12-14/test/test-12-14.cpp | 80 ++++++++++++++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 week-15/CMakeLists.txt create mode 100644 week-15/task-12-14/CMakeLists.txt create mode 100644 week-15/task-12-14/include/car_numbers.hpp create mode 100644 week-15/task-12-14/test/CMakeLists.txt create mode 100644 week-15/task-12-14/test/test-12-14.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b3d20a8..d895331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,7 @@ include_directories(shared) ##### subdirectories ##### -set(DIRS week-W week-2 week-3 week-4 week-5 week-6 week-7 week-8 week-9 week-10 week-11) +set(DIRS week-W week-2 week-3 week-4 week-5 week-6 week-7 week-8 week-9 week-10 week-11 week-15) foreach(DIR ${DIRS}) add_subdirectory(${DIR}) endforeach() diff --git a/week-15/CMakeLists.txt b/week-15/CMakeLists.txt new file mode 100644 index 0000000..eb28452 --- /dev/null +++ b/week-15/CMakeLists.txt @@ -0,0 +1,4 @@ +set(DIRS task-12-14) +foreach(DIR ${DIRS}) + add_subdirectory(${DIR}) +endforeach() diff --git a/week-15/task-12-14/CMakeLists.txt b/week-15/task-12-14/CMakeLists.txt new file mode 100644 index 0000000..0857059 --- /dev/null +++ b/week-15/task-12-14/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories(include) + +add_subdirectory(test) diff --git a/week-15/task-12-14/include/car_numbers.hpp b/week-15/task-12-14/include/car_numbers.hpp new file mode 100644 index 0000000..32887c7 --- /dev/null +++ b/week-15/task-12-14/include/car_numbers.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include + +std::vector rus_car_numbers(const std::string& str) { + const std::regex pattern(R"(\b[ABEKMHOPCTYX]\d{3}[ABEKMHOPCTYX]{2}\b)"); + std::vector numbers; + std::ranges::for_each(std::sregex_iterator(std::cbegin(str), std::cend(str), pattern), + std::sregex_iterator(), + [&numbers](const auto& matches) { numbers.push_back(matches[0]); }); + return numbers; +} diff --git a/week-15/task-12-14/test/CMakeLists.txt b/week-15/task-12-14/test/CMakeLists.txt new file mode 100644 index 0000000..ad6db21 --- /dev/null +++ b/week-15/task-12-14/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-12-14.cpp) diff --git a/week-15/task-12-14/test/test-12-14.cpp b/week-15/task-12-14/test/test-12-14.cpp new file mode 100644 index 0000000..22a6ba9 --- /dev/null +++ b/week-15/task-12-14/test/test-12-14.cpp @@ -0,0 +1,80 @@ +#include "car_numbers.hpp" +#include "gtest/gtest.h" + +TEST(RusCarNumbersTest, EmptyInput) { + EXPECT_EQ(rus_car_numbers(""), std::vector({})); +} + +TEST(RusCarNumbersTest, NoNumbersInText) { + const std::string text = R"( + Just text without license plates. + Examples: 12345, ABCDEF, !@#$%^&* + Similar looking: AB12CD, X9Y9Z9 + )"; + EXPECT_EQ(rus_car_numbers(text), std::vector({})); +} + +TEST(RusCarNumbersTest, ValidNumbersWithoutRegion) { + const std::string text = R"( + Correct plates: A123BC, E555EE, K321AM + Parking spots: M111MM and T444YX + With trash: A123BC45 -> A123BC + Almost valid: X999XZ -> invalid (Z) + )"; + EXPECT_EQ(rus_car_numbers(text), std::vector({"A123BC", "E555EE", "K321AM", + "M111MM", "T444YX", "A123BC"})); +} + +TEST(RusCarNumbersTest, InvalidFormats) { + const std::string text = R"( + Invalid: + ABC123 (wrong order) + 123ABC (starts with digits) + A1B2C3 (alternating) + A123BC45 (with region, but takes A123BC) + A12BC (too short) + Z999ZZ (invalid letter Z) + )"; + EXPECT_EQ(rus_car_numbers(text), std::vector({"A123BC"})); +} + +TEST(RusCarNumbersTest, MixedContent) { + const std::string text = R"( + Valid: A777BC and C666CT + Invalid: AB123C, X9Y9Z9 + With region: T123TX42 -> T123TX + Trash: !Y222YA@ + Almost valid: O999OI -> invalid (I) + )"; + EXPECT_EQ(rus_car_numbers(text), + std::vector({"A777BC", "C666CT", "T123TX", "Y222YA"})); +} + +TEST(RusCarNumbersTest, EdgeCases) { + const std::string text = R"( + Edge cases: + A001AX (minimum) + X999XX (maximum) + B000TB (zeros in digits) + H555HH (same letters) + Almost valid: + A000AA0 (extra zero) + Y123YF (invalid F) + )"; + EXPECT_EQ(rus_car_numbers(text), + std::vector({"A001AX", "X999XX", "B000TB", "H555HH"})); +} + +TEST(RusCarNumbersTest, AllValidLettersCombinations) { + const std::string text = R"( + All valid letters: + A123BC, B456TE, E789KX + K321AM, M111MO, H555OP + O999PC, P777CT, C666TY + T444YX, Y222XA, X888AB + )"; + EXPECT_EQ( + rus_car_numbers(text), + std::vector({"A123BC", "B456TE", "E789KX", "K321AM", "M111MO", "H555OP", + "O999PC", "P777CT", "C666TY", "T444YX", "Y222XA", "X888AB"})); +} \ No newline at end of file From 1640617f483e6f0dc739744fa55a0912395ac586 Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Sun, 6 Apr 2025 15:43:21 +0300 Subject: [PATCH 2/4] add task 12.15 --- week-15/CMakeLists.txt | 2 +- week-15/task-12-15/CMakeLists.txt | 3 ++ week-15/task-12-15/include/emails.hpp | 16 +++++++ week-15/task-12-15/test/CMakeLists.txt | 1 + week-15/task-12-15/test/test-12-15.cpp | 65 ++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 week-15/task-12-15/CMakeLists.txt create mode 100644 week-15/task-12-15/include/emails.hpp create mode 100644 week-15/task-12-15/test/CMakeLists.txt create mode 100644 week-15/task-12-15/test/test-12-15.cpp diff --git a/week-15/CMakeLists.txt b/week-15/CMakeLists.txt index eb28452..f023038 100644 --- a/week-15/CMakeLists.txt +++ b/week-15/CMakeLists.txt @@ -1,4 +1,4 @@ -set(DIRS task-12-14) +set(DIRS task-12-14 task-12-15) foreach(DIR ${DIRS}) add_subdirectory(${DIR}) endforeach() diff --git a/week-15/task-12-15/CMakeLists.txt b/week-15/task-12-15/CMakeLists.txt new file mode 100644 index 0000000..0857059 --- /dev/null +++ b/week-15/task-12-15/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories(include) + +add_subdirectory(test) diff --git a/week-15/task-12-15/include/emails.hpp b/week-15/task-12-15/include/emails.hpp new file mode 100644 index 0000000..7095251 --- /dev/null +++ b/week-15/task-12-15/include/emails.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include + +std::vector emails(const std::string& str) { + const std::regex pattern(R"(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{1,}\b)"); + std::vector emails; + std::ranges::for_each(std::sregex_iterator(std::cbegin(str), std::cend(str), pattern), + std::sregex_iterator(), + [&emails](const auto& matches) { emails.push_back(matches[0]); }); + return emails; +} diff --git a/week-15/task-12-15/test/CMakeLists.txt b/week-15/task-12-15/test/CMakeLists.txt new file mode 100644 index 0000000..5629cf8 --- /dev/null +++ b/week-15/task-12-15/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-12-15.cpp) diff --git a/week-15/task-12-15/test/test-12-15.cpp b/week-15/task-12-15/test/test-12-15.cpp new file mode 100644 index 0000000..12611dc --- /dev/null +++ b/week-15/task-12-15/test/test-12-15.cpp @@ -0,0 +1,65 @@ +#include "emails.hpp" +#include "gtest/gtest.h" + +TEST(EmailExtractionTest, EmptyInput) { EXPECT_EQ(emails(""), std::vector({})); } + +TEST(EmailExtractionTest, NoEmailsInText) { + const std::string text = R"( + This is just regular text without any emails. + Some symbols: @hello, test@, @test.com + Numbers: 12345, random!text + )"; + EXPECT_EQ(emails(text), std::vector({})); +} + +TEST(EmailExtractionTest, ValidEmails) { + const std::string text = R"( + Contact us at: john.doe@example.com or support@company.org. + Student emails: student_2023@university.edu, a.b@c.d.xyz + Special cases: email+filter@gmail.com, name@sub.domain.co.uk + )"; + EXPECT_EQ(emails(text), + std::vector({"john.doe@example.com", "support@company.org", + "student_2023@university.edu", "a.b@c.d.xyz", + "email+filter@gmail.com", "name@sub.domain.co.uk"})); +} + +TEST(EmailExtractionTest, InvalidEmails) { + const std::string text = R"( + Invalid formats: + plaintext + @missing.start + missing@at + double@@sign.com + invalid@chars_here.com + missing@tld. + )"; + EXPECT_EQ(emails(text), std::vector({})); +} + +TEST(EmailExtractionTest, MixedContent) { + const std::string text = R"( + Valid: contact@valid.com, another@good.org + Invalid: bad@email, @wrong.com + Almost valid: almost@valid. but with space + Trash: !@#$%^&* + Valid but tricky: "spaces@around.com" (should extract without quotes) + )"; + EXPECT_EQ(emails(text), std::vector( + {"contact@valid.com", "another@good.org", "spaces@around.com"})); +} + +TEST(EmailExtractionTest, EdgeCases) { + const std::string text = R"( + Minimal: a@b.c + Long domain: email@sub.sub2.sub3.example.com + Special chars: user+filter@domain.com + Numbers: 1234@numbers.com + Hyphens: hyphen-ated@domain-name.com + Quotes: "email@inside.quotes" + )"; + EXPECT_EQ(emails(text), + std::vector({"a@b.c", "email@sub.sub2.sub3.example.com", + "user+filter@domain.com", "1234@numbers.com", + "hyphen-ated@domain-name.com", "email@inside.quotes"})); +} From 28b3c0b177d0de739593978bde97f7782a54c6a8 Mon Sep 17 00:00:00 2001 From: Roman Korostinskiy <70313618+c71n93@users.noreply.github.com> Date: Sun, 6 Apr 2025 18:26:17 +0300 Subject: [PATCH 3/4] initial calculator --- week-15/CMakeLists.txt | 2 +- week-15/task-12-20/CMakeLists.txt | 5 +++ week-15/task-12-20/include/calculator.hpp | 46 +++++++++++++++++++++++ week-15/task-12-20/main.cpp | 5 +++ week-15/task-12-20/test/CMakeLists.txt | 1 + week-15/task-12-20/test/test-12-20.cpp | 43 +++++++++++++++++++++ 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 week-15/task-12-20/CMakeLists.txt create mode 100644 week-15/task-12-20/include/calculator.hpp create mode 100644 week-15/task-12-20/main.cpp create mode 100644 week-15/task-12-20/test/CMakeLists.txt create mode 100644 week-15/task-12-20/test/test-12-20.cpp diff --git a/week-15/CMakeLists.txt b/week-15/CMakeLists.txt index f023038..139200d 100644 --- a/week-15/CMakeLists.txt +++ b/week-15/CMakeLists.txt @@ -1,4 +1,4 @@ -set(DIRS task-12-14 task-12-15) +set(DIRS task-12-14 task-12-15 task-12-20) foreach(DIR ${DIRS}) add_subdirectory(${DIR}) endforeach() diff --git a/week-15/task-12-20/CMakeLists.txt b/week-15/task-12-20/CMakeLists.txt new file mode 100644 index 0000000..3cf81ed --- /dev/null +++ b/week-15/task-12-20/CMakeLists.txt @@ -0,0 +1,5 @@ +include_directories(include) + +add_subdirectory(test) + +add_executable(ae main.cpp) diff --git a/week-15/task-12-20/include/calculator.hpp b/week-15/task-12-20/include/calculator.hpp new file mode 100644 index 0000000..8a8eddc --- /dev/null +++ b/week-15/task-12-20/include/calculator.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +bool is_operator(char c) { + return c == '+' || c == '-' || c == '*' || c == '/'; +} + +int calculate(const std::string& str) { + std::stack stack; + for (auto it = std::cbegin(str); it != std::cend(str);) { + if (static_cast(std::isspace(*it))) { + while (it != std::cend(str) && static_cast(std::isspace(*it))) { ++it; } + } else if (static_cast(std::isdigit(*it))) { + std::istringstream iss(std::string(it, std::cend(str))); + int val; + iss >> val; + stack.push(val); + std::advance(it, iss.tellg()); + } else if (is_operator(*it)) { + const int b = stack.top(); stack.pop(); + const int a = stack.top(); stack.pop(); + switch (*it) { + case '+': + stack.push(a + b); + break; + case '-': + stack.push(a - b); + break; + case '*': + stack.push(a * b); + break; + case '/': + stack.push(a / b); + break; + default: + throw std::runtime_error("unexpected operator"); + } + ++it; + } else { + throw std::runtime_error("unexpected symbol"); + } + } + return stack.top(); +} diff --git a/week-15/task-12-20/main.cpp b/week-15/task-12-20/main.cpp new file mode 100644 index 0000000..6624d47 --- /dev/null +++ b/week-15/task-12-20/main.cpp @@ -0,0 +1,5 @@ +#include "calculator.hpp" + +int main() { + calculate("3 4 +"); +} diff --git a/week-15/task-12-20/test/CMakeLists.txt b/week-15/task-12-20/test/CMakeLists.txt new file mode 100644 index 0000000..ccd0836 --- /dev/null +++ b/week-15/task-12-20/test/CMakeLists.txt @@ -0,0 +1 @@ +cpp_test(test-12-20.cpp) diff --git a/week-15/task-12-20/test/test-12-20.cpp b/week-15/task-12-20/test/test-12-20.cpp new file mode 100644 index 0000000..1984c29 --- /dev/null +++ b/week-15/task-12-20/test/test-12-20.cpp @@ -0,0 +1,43 @@ +#include "gtest/gtest.h" +#include "calculator.hpp" + +TEST(RPNCalculatorTest, BasicOperations) { + EXPECT_EQ(calculate("3 4 +"), 7); // 3 + 4 + EXPECT_EQ(calculate("5 2 -"), 3); // 5 - 2 + EXPECT_EQ(calculate("2 3 *"), 6); // 2 * 3 + EXPECT_EQ(calculate("6 2 /"), 3); // 6 / 2 +} + +TEST(RPNCalculatorTest, ComplexExpressions) { + EXPECT_EQ(calculate("3 4 2 * +"), 11); // 3 + (4 * 2) + EXPECT_EQ(calculate("5 1 2 + 4 * + 3 -"), 14); // 5 + ((1+2)*4) - 3 + EXPECT_EQ(calculate("4 2 5 * + 1 3 - /"), -7); // (4 + 2*5) / (1-3) +} + +TEST(RPNCalculatorTest, EdgeCases) { + EXPECT_EQ(calculate("0 5 +"), 5); // 0 + 5 + EXPECT_EQ(calculate("1 1 -"), 0); // 1 - 1 + EXPECT_EQ(calculate("999 1 +"), 1000); // large number + EXPECT_EQ(calculate("-5 3 +"), -2); // negative number +} + +TEST(RPNCalculatorTest, InvalidInput) { + EXPECT_THROW(calculate(""), std::invalid_argument); // empty + EXPECT_THROW(calculate("3 +"), std::invalid_argument); // missing operand + EXPECT_THROW(calculate("3 4 + -"), std::invalid_argument); // extra operator + EXPECT_THROW(calculate("a b +"), std::invalid_argument); // non-numbers + EXPECT_THROW(calculate("3 0 /"), std::runtime_error); // division by zero +} + +TEST(RPNCalculatorTest, WhitespaceHandling) { + EXPECT_EQ(calculate(" 3 4 + "), 7); // extra spaces + EXPECT_EQ(calculate("3\n4\t+"), 7); // different whitespace + EXPECT_EQ(calculate("3 4 + "), 7); // trailing space +} + +TEST(RPNCalculatorTest, MultiDigitNumbers) { + EXPECT_EQ(calculate("10 20 +"), 30); // 10 + 20 + EXPECT_EQ(calculate("100 50 -"), 50); // 100 - 50 + EXPECT_EQ(calculate("12 34 *"), 408); // 12 * 34 + EXPECT_EQ(calculate("100 5 /"), 20); // 100 / 5 +} From eef023f70f8d724959f4ca0a83f1569c9ff779da Mon Sep 17 00:00:00 2001 From: c71n93 Date: Wed, 9 Apr 2025 19:06:05 +0300 Subject: [PATCH 4/4] task 12.20: calculator --- week-15/task-12-20/CMakeLists.txt | 2 - week-15/task-12-20/include/calculator.hpp | 53 +++++++++++------- week-15/task-12-20/main.cpp | 5 -- week-15/task-12-20/test/test-12-20.cpp | 68 +++++++++++++++-------- 4 files changed, 77 insertions(+), 51 deletions(-) delete mode 100644 week-15/task-12-20/main.cpp diff --git a/week-15/task-12-20/CMakeLists.txt b/week-15/task-12-20/CMakeLists.txt index 3cf81ed..0857059 100644 --- a/week-15/task-12-20/CMakeLists.txt +++ b/week-15/task-12-20/CMakeLists.txt @@ -1,5 +1,3 @@ include_directories(include) add_subdirectory(test) - -add_executable(ae main.cpp) diff --git a/week-15/task-12-20/include/calculator.hpp b/week-15/task-12-20/include/calculator.hpp index 8a8eddc..6405a51 100644 --- a/week-15/task-12-20/include/calculator.hpp +++ b/week-15/task-12-20/include/calculator.hpp @@ -1,27 +1,41 @@ #pragma once -#include +#include #include +#include -bool is_operator(char c) { +inline bool is_operator(const std::string& str) { + if (str.size() != 1) { + return false; + } + const char c = str[0]; return c == '+' || c == '-' || c == '*' || c == '/'; } -int calculate(const std::string& str) { - std::stack stack; - for (auto it = std::cbegin(str); it != std::cend(str);) { - if (static_cast(std::isspace(*it))) { - while (it != std::cend(str) && static_cast(std::isspace(*it))) { ++it; } - } else if (static_cast(std::isdigit(*it))) { - std::istringstream iss(std::string(it, std::cend(str))); - int val; - iss >> val; - stack.push(val); - std::advance(it, iss.tellg()); - } else if (is_operator(*it)) { - const int b = stack.top(); stack.pop(); - const int a = stack.top(); stack.pop(); - switch (*it) { +inline bool may_be_number(const std::string& str) { + return (str[0] == '-' && str.size() > 1) || static_cast(std::isdigit(str[0])); +} + +double calculate(const std::string& str) { + if (str.empty()) { + throw std::invalid_argument("empty string"); + } + std::stack stack; + std::istringstream iss(str); + std::string token; + while (iss >> token) { + assert(!token.empty()); + if (may_be_number(token)) { + stack.push(std::stod(token)); + } else if (is_operator(token)) { + if (stack.size() < 2) { + throw std::invalid_argument("too few arguments for operation"); + } + const double b = stack.top(); + stack.pop(); + const double a = stack.top(); + stack.pop(); + switch (token[0]) { case '+': stack.push(a + b); break; @@ -35,11 +49,10 @@ int calculate(const std::string& str) { stack.push(a / b); break; default: - throw std::runtime_error("unexpected operator"); + throw std::invalid_argument("unexpected operator"); } - ++it; } else { - throw std::runtime_error("unexpected symbol"); + throw std::invalid_argument("unexpected token"); } } return stack.top(); diff --git a/week-15/task-12-20/main.cpp b/week-15/task-12-20/main.cpp deleted file mode 100644 index 6624d47..0000000 --- a/week-15/task-12-20/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "calculator.hpp" - -int main() { - calculate("3 4 +"); -} diff --git a/week-15/task-12-20/test/test-12-20.cpp b/week-15/task-12-20/test/test-12-20.cpp index 1984c29..9b12a4c 100644 --- a/week-15/task-12-20/test/test-12-20.cpp +++ b/week-15/task-12-20/test/test-12-20.cpp @@ -1,43 +1,63 @@ -#include "gtest/gtest.h" #include "calculator.hpp" +#include "gtest/gtest.h" TEST(RPNCalculatorTest, BasicOperations) { - EXPECT_EQ(calculate("3 4 +"), 7); // 3 + 4 - EXPECT_EQ(calculate("5 2 -"), 3); // 5 - 2 - EXPECT_EQ(calculate("2 3 *"), 6); // 2 * 3 - EXPECT_EQ(calculate("6 2 /"), 3); // 6 / 2 + EXPECT_DOUBLE_EQ(calculate("3 4 +"), 7.0); + EXPECT_DOUBLE_EQ(calculate("5 2 -"), 3.0); + EXPECT_DOUBLE_EQ(calculate("2 3 *"), 6.0); + EXPECT_DOUBLE_EQ(calculate("6 2 /"), 3.0); +} + +TEST(RPNCalculatorTest, FloatingPointOperations) { + EXPECT_DOUBLE_EQ(calculate("3.5 4.2 +"), 7.7); + EXPECT_DOUBLE_EQ(calculate("5.1 2.3 -"), 2.8); + EXPECT_DOUBLE_EQ(calculate("2.5 3.2 *"), 8.0); + EXPECT_DOUBLE_EQ(calculate("7.5 2.5 /"), 3.0); } TEST(RPNCalculatorTest, ComplexExpressions) { - EXPECT_EQ(calculate("3 4 2 * +"), 11); // 3 + (4 * 2) - EXPECT_EQ(calculate("5 1 2 + 4 * + 3 -"), 14); // 5 + ((1+2)*4) - 3 - EXPECT_EQ(calculate("4 2 5 * + 1 3 - /"), -7); // (4 + 2*5) / (1-3) + EXPECT_DOUBLE_EQ(calculate("3 4 2 * +"), 11.0); + EXPECT_DOUBLE_EQ(calculate("5 1 2 + 4 * + 3 -"), 14.0); + EXPECT_DOUBLE_EQ(calculate("4 2 5 * + 1 3 - /"), -7.0); } TEST(RPNCalculatorTest, EdgeCases) { - EXPECT_EQ(calculate("0 5 +"), 5); // 0 + 5 - EXPECT_EQ(calculate("1 1 -"), 0); // 1 - 1 - EXPECT_EQ(calculate("999 1 +"), 1000); // large number - EXPECT_EQ(calculate("-5 3 +"), -2); // negative number + EXPECT_DOUBLE_EQ(calculate("0 5 +"), 5.0); + EXPECT_DOUBLE_EQ(calculate("1 1 -"), 0.0); + EXPECT_DOUBLE_EQ(calculate("999 1 +"), 1000.0); + EXPECT_DOUBLE_EQ(calculate("-5 3 +"), -2.0); } TEST(RPNCalculatorTest, InvalidInput) { - EXPECT_THROW(calculate(""), std::invalid_argument); // empty - EXPECT_THROW(calculate("3 +"), std::invalid_argument); // missing operand - EXPECT_THROW(calculate("3 4 + -"), std::invalid_argument); // extra operator - EXPECT_THROW(calculate("a b +"), std::invalid_argument); // non-numbers - EXPECT_THROW(calculate("3 0 /"), std::runtime_error); // division by zero + EXPECT_THROW(calculate(""), std::invalid_argument); + EXPECT_THROW(calculate("3 +"), std::invalid_argument); + EXPECT_THROW(calculate("3 4 + -"), std::invalid_argument); + EXPECT_THROW(calculate("a b +"), std::invalid_argument); } TEST(RPNCalculatorTest, WhitespaceHandling) { - EXPECT_EQ(calculate(" 3 4 + "), 7); // extra spaces - EXPECT_EQ(calculate("3\n4\t+"), 7); // different whitespace - EXPECT_EQ(calculate("3 4 + "), 7); // trailing space + EXPECT_DOUBLE_EQ(calculate(" 3 4 + "), 7.0); + EXPECT_DOUBLE_EQ(calculate("3\n4\t+"), 7.0); + EXPECT_DOUBLE_EQ(calculate("3 4 + "), 7.0); } TEST(RPNCalculatorTest, MultiDigitNumbers) { - EXPECT_EQ(calculate("10 20 +"), 30); // 10 + 20 - EXPECT_EQ(calculate("100 50 -"), 50); // 100 - 50 - EXPECT_EQ(calculate("12 34 *"), 408); // 12 * 34 - EXPECT_EQ(calculate("100 5 /"), 20); // 100 / 5 + EXPECT_DOUBLE_EQ(calculate("10 20 +"), 30.0); + EXPECT_DOUBLE_EQ(calculate("100 50 -"), 50.0); + EXPECT_DOUBLE_EQ(calculate("12 34 *"), 408.0); + EXPECT_DOUBLE_EQ(calculate("100 5 /"), 20.0); +} + +TEST(RPNCalculatorTest, PrecisionHandling) { + EXPECT_DOUBLE_EQ(calculate("0.1 0.2 +"), 0.3); + EXPECT_DOUBLE_EQ(calculate("1.234 2.345 +"), 3.579); + EXPECT_NEAR(calculate("1.0 3.0 /"), 0.333333, 1e-6); + EXPECT_NEAR(calculate("2.0 3.0 /"), 0.666666, 1e-6); +} + +TEST(RPNCalculatorTest, MixedIntegerAndFloatingPoint) { + EXPECT_DOUBLE_EQ(calculate("3 4.5 +"), 7.5); + EXPECT_DOUBLE_EQ(calculate("5.2 2 -"), 3.2); + EXPECT_DOUBLE_EQ(calculate("2 3.5 *"), 7.0); + EXPECT_DOUBLE_EQ(calculate("6.6 2.2 /"), 3.0); }