Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions week-15/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set(DIRS task-12-14 task-12-15 task-12-20)
foreach(DIR ${DIRS})
add_subdirectory(${DIR})
endforeach()
3 changes: 3 additions & 0 deletions week-15/task-12-14/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include_directories(include)

add_subdirectory(test)
16 changes: 16 additions & 0 deletions week-15/task-12-14/include/car_numbers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <algorithm>
#include <ranges>
#include <regex>
#include <string>
#include <vector>

std::vector<std::string> rus_car_numbers(const std::string& str) {
const std::regex pattern(R"(\b[ABEKMHOPCTYX]\d{3}[ABEKMHOPCTYX]{2}\b)");
std::vector<std::string> 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;
}
1 change: 1 addition & 0 deletions week-15/task-12-14/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpp_test(test-12-14.cpp)
80 changes: 80 additions & 0 deletions week-15/task-12-14/test/test-12-14.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "car_numbers.hpp"
#include "gtest/gtest.h"

TEST(RusCarNumbersTest, EmptyInput) {
EXPECT_EQ(rus_car_numbers(""), std::vector<std::string>({}));
}

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<std::string>({}));
}

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<std::string>({"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<std::string>({"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<std::string>({"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<std::string>({"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<std::string>({"A123BC", "B456TE", "E789KX", "K321AM", "M111MO", "H555OP",
"O999PC", "P777CT", "C666TY", "T444YX", "Y222XA", "X888AB"}));
}
3 changes: 3 additions & 0 deletions week-15/task-12-15/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include_directories(include)

add_subdirectory(test)
16 changes: 16 additions & 0 deletions week-15/task-12-15/include/emails.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <algorithm>
#include <ranges>
#include <regex>
#include <string>
#include <vector>

std::vector<std::string> 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<std::string> 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;
}
1 change: 1 addition & 0 deletions week-15/task-12-15/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpp_test(test-12-15.cpp)
65 changes: 65 additions & 0 deletions week-15/task-12-15/test/test-12-15.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "emails.hpp"
#include "gtest/gtest.h"

TEST(EmailExtractionTest, EmptyInput) { EXPECT_EQ(emails(""), std::vector<std::string>({})); }

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<std::string>({}));
}

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<std::string>({"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<std::string>({}));
}

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<std::string>(
{"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<std::string>({"a@b.c", "email@sub.sub2.sub3.example.com",
"user+filter@domain.com", "1234@numbers.com",
"hyphen-ated@domain-name.com", "email@inside.quotes"}));
}
3 changes: 3 additions & 0 deletions week-15/task-12-20/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include_directories(include)

add_subdirectory(test)
59 changes: 59 additions & 0 deletions week-15/task-12-20/include/calculator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include <cassert>
#include <sstream>
#include <stack>

inline bool is_operator(const std::string& str) {
if (str.size() != 1) {
return false;
}
const char c = str[0];
return c == '+' || c == '-' || c == '*' || c == '/';
}

inline bool may_be_number(const std::string& str) {
return (str[0] == '-' && str.size() > 1) || static_cast<bool>(std::isdigit(str[0]));
}

double calculate(const std::string& str) {
if (str.empty()) {
throw std::invalid_argument("empty string");
}
std::stack<double> 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;
case '-':
stack.push(a - b);
break;
case '*':
stack.push(a * b);
break;
case '/':
stack.push(a / b);
break;
default:
throw std::invalid_argument("unexpected operator");
}
} else {
throw std::invalid_argument("unexpected token");
}
}
return stack.top();
}
1 change: 1 addition & 0 deletions week-15/task-12-20/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpp_test(test-12-20.cpp)
63 changes: 63 additions & 0 deletions week-15/task-12-20/test/test-12-20.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "calculator.hpp"
#include "gtest/gtest.h"

TEST(RPNCalculatorTest, BasicOperations) {
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_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_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);
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_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_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);
}