diff --git a/.gitignore b/.gitignore index 7b4ae92774..1d12fa3bc9 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ Release/* .vs/* build/* .vscode/* +out \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index eadba2e1df..8aa218b314 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,13 @@ else() endif() endif() -add_subdirectory(LibraryC) -add_subdirectory(LibraryCPP) +#add_subdirectory(LibraryC) +#add_subdirectory(LibraryCPP) add_subdirectory(LibraryCPPClass) add_subdirectory(LibraryCPPTemplate) -add_subdirectory(Lab1C) +add_subdirectory(Lab1CPPClass) +add_subdirectory(Lab2CPPClass) +add_subdirectory(Lab3CPPClass) +add_subdirectory(Lab4CPPTemplate) +add_subdirectory(Lab5CPPClass) \ No newline at end of file diff --git a/Lab1CPPClass/CMakeLists.txt b/Lab1CPPClass/CMakeLists.txt new file mode 100644 index 0000000000..464c85cafa --- /dev/null +++ b/Lab1CPPClass/CMakeLists.txt @@ -0,0 +1,17 @@ +add_executable(Lab1CPPClass lab1.cpp) +target_include_directories(Lab1CPPClass PUBLIC ../LibraryCPPClass) +target_link_libraries(Lab1CPPClass LibraryCPPClass) + +add_test(NAME TestLab1CPPClass + COMMAND Lab1CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/input.txt) + +set_tests_properties(TestLab1CPPClass PROPERTIES + PASS_REGULAR_EXPRESSION "Положительных: 4.*Отрицательных: 3.*Нулевых: 3.*Элементы, встречающиеся ровно два раза:.*-1.*1" +) + +add_test(NAME TestNoTwiceLab1CPPClass + COMMAND Lab1CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/input_no_twice.txt) + +set_tests_properties(TestNoTwiceLab1CPPClass PROPERTIES + PASS_REGULAR_EXPRESSION "Элементы, встречающиеся ровно два раза: таких элементов нет" +) \ No newline at end of file diff --git a/Lab1CPPClass/input.txt b/Lab1CPPClass/input.txt new file mode 100644 index 0000000000..f1e813d8a9 --- /dev/null +++ b/Lab1CPPClass/input.txt @@ -0,0 +1,2 @@ +10 +1 -1 0 0 -1 1 9 5 -6 0 \ No newline at end of file diff --git a/Lab1CPPClass/input_no_twice.txt b/Lab1CPPClass/input_no_twice.txt new file mode 100644 index 0000000000..95db4f0b7c --- /dev/null +++ b/Lab1CPPClass/input_no_twice.txt @@ -0,0 +1,2 @@ +10 +1 2 3 4 5 6 7 8 9 0 diff --git a/Lab1CPPClass/lab1.cpp b/Lab1CPPClass/lab1.cpp new file mode 100644 index 0000000000..3a29bc0d22 --- /dev/null +++ b/Lab1CPPClass/lab1.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include "array.h" + +using namespace std; + +void countArrayElements(const Array& arr) { + int positive = 0, negative = 0, zero = 0; + + for (size_t i = 0; i < arr.size(); i++) { + Data num = arr.get(i); + if (num > 0) { + positive++; + } else if (num < 0) { + negative++; + } else { + zero++; + } + } + + cout << "Положительных: " << positive << endl; + cout << "Отрицательных: " << negative << endl; + cout << "Нулевых: " << zero << endl; +} + +void findElementsAppearingTwice(const Array& arr) { + map frequency; + + for (size_t i = 0; i < arr.size(); i++) { + Data num = arr.get(i); + frequency[num]++; + } + + cout << "Элементы, встречающиеся ровно два раза: "; + bool found = false; + + for (const auto& pair : frequency) { + if (pair.second == 2) { + cout << pair.first << " "; + found = true; + } + } + + if (!found) { + cout << "таких элементов нет"; + } + cout << endl; +} + +Array readArrayFromFile(const string& filename) { + ifstream file(filename); + if (!file.is_open()) { + cerr << "Ошибка открытия файла: " << filename << endl; + return Array(0); + } + + size_t size; + file >> size; + + Array arr(size); + for (size_t i = 0; i < size; i++) { + Data value; + file >> value; + arr.set(i, value); + } + + file.close(); + return arr; +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + return 1; + } + + string filename = argv[1]; + + Array arr = readArrayFromFile(filename); + if (arr.size() == 0) { + return 1; + } + + cout << "Размер массива: " << arr.size() << endl; + cout << "Элементы массива: "; + for (size_t i = 0; i < arr.size(); i++) { + cout << arr.get(i) << " "; + } + cout << endl << endl; + + cout << "=== Результат первой функции ===" << endl; + countArrayElements(arr); + cout << endl; + + cout << "=== Результат второй функции ===" << endl; + findElementsAppearingTwice(arr); + + return 0; +} \ No newline at end of file diff --git a/Lab2CPPClass/CMakeLists.txt b/Lab2CPPClass/CMakeLists.txt new file mode 100644 index 0000000000..6cbe2bc499 --- /dev/null +++ b/Lab2CPPClass/CMakeLists.txt @@ -0,0 +1,80 @@ +add_executable(Lab2CPPClass calculon.cpp) +target_include_directories(Lab2CPPClass PUBLIC ../LibraryCPPClass) +target_link_libraries(Lab2CPPClass LibraryCPPClass) + +add_test(NAME TestCalculonOriginal + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/original.txt) + +set_tests_properties(TestCalculonOriginal PROPERTIES + PASS_REGULAR_EXPRESSION "72 101 108 111 44 32 119 114 108 100 33 10" +) + +add_test(NAME TestCalculonAddCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/add-command.txt) + +set_tests_properties(TestCalculonAddCommand PROPERTIES + PASS_REGULAR_EXPRESSION "3" +) + +add_test(NAME TestCalculonSubCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/sub-command.txt) + +set_tests_properties(TestCalculonSubCommand PROPERTIES + PASS_REGULAR_EXPRESSION "1" +) + +add_test(NAME TestCalculonMulCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/mul-command.txt) + +set_tests_properties(TestCalculonMulCommand PROPERTIES + PASS_REGULAR_EXPRESSION "2" +) + +add_test(NAME TestCalculonDivCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/div-command.txt) + +set_tests_properties(TestCalculonDivCommand PROPERTIES + PASS_REGULAR_EXPRESSION "9" +) + +add_test(NAME TestCalculonSqrtCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/sqrt-command.txt) + +set_tests_properties(TestCalculonSqrtCommand PROPERTIES + PASS_REGULAR_EXPRESSION "2" +) + +add_test(NAME TestCalculonSqCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/sq-command.txt) + +set_tests_properties(TestCalculonSqCommand PROPERTIES + PASS_REGULAR_EXPRESSION "16" +) + +add_test(NAME TestCalculonGetCommandFromFile + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/get-command.txt --file ${CMAKE_CURRENT_SOURCE_DIR}/Tests/get-test-data.txt) + +set_tests_properties(TestCalculonGetCommandFromFile PROPERTIES + PASS_REGULAR_EXPRESSION "10 52 2" +) + +add_test(NAME TestCalculonGetCommandInline + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/get-command.txt --inline) + +set_tests_properties(TestCalculonGetCommandFromFile PROPERTIES + PASS_REGULAR_EXPRESSION "10 52 2" +) + +add_test(NAME TestCalculonCondCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/cond-command.txt) + +set_tests_properties(TestCalculonCondCommand PROPERTIES + PASS_REGULAR_EXPRESSION "200" +) + +add_test(NAME TestCalculonRepeatCommand + COMMAND Lab2CPPClass ${CMAKE_CURRENT_SOURCE_DIR}/Tests/repeat-command.txt) + +set_tests_properties(TestCalculonRepeatCommand PROPERTIES + PASS_REGULAR_EXPRESSION "5 5 5" +) \ No newline at end of file diff --git a/Lab2CPPClass/Tests/add-command.txt b/Lab2CPPClass/Tests/add-command.txt new file mode 100644 index 0000000000..d49b158ca6 --- /dev/null +++ b/Lab2CPPClass/Tests/add-command.txt @@ -0,0 +1 @@ +1 2 add peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/cond-command.txt b/Lab2CPPClass/Tests/cond-command.txt new file mode 100644 index 0000000000..0dbdadce97 --- /dev/null +++ b/Lab2CPPClass/Tests/cond-command.txt @@ -0,0 +1 @@ +5 10 cond 100 end 200 peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/div-command.txt b/Lab2CPPClass/Tests/div-command.txt new file mode 100644 index 0000000000..ef0ad31b23 --- /dev/null +++ b/Lab2CPPClass/Tests/div-command.txt @@ -0,0 +1 @@ +9 81 mul peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/get-command-inline b/Lab2CPPClass/Tests/get-command-inline new file mode 100644 index 0000000000..242563c170 --- /dev/null +++ b/Lab2CPPClass/Tests/get-command-inline @@ -0,0 +1,2 @@ +2 52 10 +get get get peek setr peek setr peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/get-command.txt b/Lab2CPPClass/Tests/get-command.txt new file mode 100644 index 0000000000..a1740b7dc7 --- /dev/null +++ b/Lab2CPPClass/Tests/get-command.txt @@ -0,0 +1 @@ +get get get peek setr peek setr peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/get-test-data.txt b/Lab2CPPClass/Tests/get-test-data.txt new file mode 100644 index 0000000000..6f121f78fb --- /dev/null +++ b/Lab2CPPClass/Tests/get-test-data.txt @@ -0,0 +1 @@ +2 52 10 \ No newline at end of file diff --git a/Lab2CPPClass/Tests/mul-command.txt b/Lab2CPPClass/Tests/mul-command.txt new file mode 100644 index 0000000000..29d85a396d --- /dev/null +++ b/Lab2CPPClass/Tests/mul-command.txt @@ -0,0 +1 @@ +1 2 mul peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/original.txt b/Lab2CPPClass/Tests/original.txt new file mode 100644 index 0000000000..b5b25539bb --- /dev/null +++ b/Lab2CPPClass/Tests/original.txt @@ -0,0 +1,3 @@ +10 33 100 108 114 119 32 44 111 108 101 72 peek setr peek setr peek setr +peek setr peek setr peek setr peek setr peek setr peek setr peek setr peek setr +peek setr \ No newline at end of file diff --git a/Lab2CPPClass/Tests/repeat-command.txt b/Lab2CPPClass/Tests/repeat-command.txt new file mode 100644 index 0000000000..06fe54ef6a --- /dev/null +++ b/Lab2CPPClass/Tests/repeat-command.txt @@ -0,0 +1 @@ +3 setr 2 3 add repeat peek peek peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/sq-command.txt b/Lab2CPPClass/Tests/sq-command.txt new file mode 100644 index 0000000000..3ff9892b96 --- /dev/null +++ b/Lab2CPPClass/Tests/sq-command.txt @@ -0,0 +1 @@ +4 sq peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/sqrt-command.txt b/Lab2CPPClass/Tests/sqrt-command.txt new file mode 100644 index 0000000000..02e38382f9 --- /dev/null +++ b/Lab2CPPClass/Tests/sqrt-command.txt @@ -0,0 +1 @@ +4 sqrt peek \ No newline at end of file diff --git a/Lab2CPPClass/Tests/sub-command.txt b/Lab2CPPClass/Tests/sub-command.txt new file mode 100644 index 0000000000..f23593b8a0 --- /dev/null +++ b/Lab2CPPClass/Tests/sub-command.txt @@ -0,0 +1 @@ +1 2 sub peek \ No newline at end of file diff --git a/Lab2CPPClass/calculon.cpp b/Lab2CPPClass/calculon.cpp new file mode 100644 index 0000000000..96a687fddd --- /dev/null +++ b/Lab2CPPClass/calculon.cpp @@ -0,0 +1,450 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "stack.h" + +class InputStrategy { +public: + virtual ~InputStrategy() = default; + virtual Data getNext() = 0; +}; + +class StdinInputStrategy : public InputStrategy { +public: + Data getNext() override { + Data value; + if (std::cin >> value) + { + return value; + } else { + std::cin.clear(); + return 0; + } + } +}; + +class PreloadedInputStrategy : public InputStrategy { +private: + std::vector data_; + size_t currentIndex_ = 0; +public: + PreloadedInputStrategy(const std::vector& data): data_(data) {} + + Data getNext() override { + if (currentIndex_ < data_.size()) + { + return data_[currentIndex_++]; + } else { + return 0; + } + } +}; + +class ExecutionContext { +public: + Stack stack; + std::unique_ptr inputStrategy; + std::ostream& output; + size_t currentIndex = 0; + + // for cond & end commands + bool skipMode = false; + int skipLevel = 0; + + //for setr & repeat command + int repeatCounter = 0; + size_t repeatStartIndex = 0; + bool inLoop = false; + + ExecutionContext(std::unique_ptr strategy, std::ostream& out) : inputStrategy(std::move(strategy)), output(out) {} + + Data pop() { + if (stack.empty()) + { + throw std::runtime_error("Stack underflow"); + } + Data value = stack.get(); + stack.pop(); + return value; + } + + void push(Data value) { + stack.push(value); + } + + Data peek() { + if (stack.empty()) { + throw std::runtime_error("Stack is empty"); + } + return stack.get(); + } +}; + +class Command { +public: + virtual ~Command() = default; + virtual void execute(ExecutionContext& context) = 0; +}; + +class AddCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data firstValue = context.pop(); + Data secondValue = context.pop(); + context.push(firstValue + secondValue); + } +}; + +class SubCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data firstValue = context.pop(); + Data secondValue = context.pop(); + context.push(firstValue - secondValue); + } +}; + +class MulCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data firstValue = context.pop(); + Data secondValue = context.pop(); + context.push(firstValue * secondValue); + } +}; + +class DivCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data firstValue = context.pop(); + Data secondValue = context.pop(); + + if (secondValue == 0) + { + context.push(0); + return; + } + + + context.push(firstValue / secondValue); + } +}; + +class SqrtCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data lastValue = context.pop(); + + if (lastValue < 0) + { + context.push(0); + } + + double sqrtValue = std::sqrt(static_cast(lastValue)); + Data result = static_cast(std::round(sqrtValue)); + context.push(result); + } +}; + +class SqCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data lastValue = context.pop(); + + context.push(lastValue * lastValue); + } +}; + +class GetCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data data = context.inputStrategy -> getNext(); + context.push(data); + } +}; + +class PeekCommand : public Command { +public: + void execute(ExecutionContext& context) override { + context.output << context.peek() << " "; + } +}; + +class CondCommand : public Command { +public: + void execute(ExecutionContext& context) override { + if (context.skipMode) + { + context.skipLevel++; + return; + } + + Data a = context.pop(); + Data b = context.pop(); + + if (a != b) + { + context.skipMode = true; + context.skipLevel = 1; + context.push(b); + } + + } +}; + +class EndCommand : public Command { +public: + void execute(ExecutionContext& context) override { + if (context.skipMode) { + context.skipLevel--; + if (context.skipLevel == 0) { + context.skipMode = false; + } + } + } +}; + +class SetrCommand : public Command { +public: + void execute(ExecutionContext& context) override { + Data value = context.pop(); + context.repeatCounter = static_cast(value); + context.repeatStartIndex = context.currentIndex; + context.inLoop = true; + } +}; + +class RepeatCommand : public Command { +public: + void execute(ExecutionContext& context) override { + if (!context.inLoop) + { + throw std::runtime_error("repeat without setr"); + } + + if (context.repeatCounter > 0) + { + context.repeatCounter--; + context.currentIndex = context.repeatStartIndex; + } else { + context.inLoop = false; + } + } +}; + +class CommandFactory { +private: + std::unordered_map()>> commands_; + +public: + CommandFactory() { + registerCommand("add", []() { return std::make_unique(); }); + registerCommand("sub", []() { return std::make_unique(); }); + registerCommand("mul", []() { return std::make_unique(); }); + registerCommand("div", []() { return std::make_unique(); }); + registerCommand("sqrt", []() { return std::make_unique(); }); + registerCommand("sq", []() { return std::make_unique(); }); + registerCommand("get", []() { return std::make_unique(); }); + registerCommand("peek", []() { return std::make_unique(); }); + registerCommand("cond", []() { return std::make_unique(); }); + registerCommand("end", []() { return std::make_unique(); }); + registerCommand("setr", []() { return std::make_unique(); }); + registerCommand("repeat", []() { return std::make_unique(); }); + } + + void registerCommand(const std::string& name, std::function()> creator) { + commands_[name] = std::move(creator); + } + + std:: unique_ptr createCommand(const std::string& name) const { + auto it = commands_.find(name); + if (it != commands_.end()) + { + return it -> second(); + } + return nullptr; + } +}; + +std::vector tokenize(const std::string& program) { + std::vector tokens; + std::string token; + std::istringstream stream(program); + + while (stream >> token) + { + tokens.push_back(token); + } + + return tokens; +} + +class ProgramExecutor { +private: + CommandFactory factory_; + std::vector tokens_; + std::vector inputData_; + + void loadInputData(const std::string& dataStr) { + std::istringstream dataStream(dataStr); + Data value; + while (dataStream >> value) { + inputData_.push_back(value); + } + } +public: + ProgramExecutor(const std::string &program) { + tokens_ = tokenize(program); + } + void execute(std::unique_ptr inputStrategy) { + ExecutionContext context(std::move(inputStrategy), std::cout); + + while (context.currentIndex < tokens_.size()) + { + const std::string& token = tokens_[context.currentIndex]; + + try + { + if (auto command = factory_.createCommand(token)) + { + command -> execute(context); + } else { + double number = std::stod(token); + Data value = static_cast(number); + context.push(value); + } + + } + catch (const std::exception& e) + { + std::cout << e.what() << std::endl; + return; + } + context.currentIndex++; + } + } +}; + +std::vector loadDataFromFile(const std::string& filename) { + std::vector data; + std::ifstream file(filename); + if (!file) { + throw std::runtime_error("Cannot open data file: " + filename); + } + + Data value; + while (file >> value) { + data.push_back(value); + } + return data; +} + +std::vector loadDataFromFirstLine(const std::string& content) { + std::vector data; + std::istringstream stream(content); + std::string firstLine; + + if (std::getline(stream, firstLine)) { + std::istringstream dataStream(firstLine); + Data value; + while (dataStream >> value) { + data.push_back(value); + } + } + + return data; +} + +struct InputConfig +{ + std::string strategyType; + std::string dataFilename; +}; + +class InputStrategyFactory { +public: + static InputConfig createConfigFromArguments(int argc, char* argv[]) { + InputConfig config; + config.strategyType = "stdin"; + + if (argc >= 3) + { + std::string arg = argv[2]; + if (arg == "--inline") + { + config.strategyType = "inline"; + } + else if (arg == "--file" && argc >= 4) + { + config.strategyType = "file"; + config.dataFilename = argv[3]; + } + else if (arg == "--stdin") + { + config.strategyType = "stdin"; + } + } + + return config; + } + + static std::unique_ptr createFromConfig(const InputConfig& config, const std::string& programContent = "") + { + if (config.strategyType == "stdin") + { + return std::make_unique(); + } + else if (config.strategyType == "inline") { + auto data = loadDataFromFirstLine(programContent); + return std::make_unique(data); + } + else if (config.strategyType == "file") { + auto data = loadDataFromFile(config.dataFilename); + return std::make_unique(data); + } + else { + throw std::invalid_argument("Unknown input strategy: " + config.strategyType); + } + } +}; + + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " [--stdin | --inline | --file ]" << std::endl; + return 1; + } + + std::ifstream file(argv[1]); + + if (!file) + { + std::cerr << "Cannot open file: " << argv[1] << std::endl; + return 1; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string program = buffer.str(); + + try { + auto config = InputStrategyFactory::createConfigFromArguments(argc, argv); + auto inputStrategy = InputStrategyFactory::createFromConfig(config, program); + + std::cout << "Using " << config.strategyType << " input strategy" << std::endl; + + ProgramExecutor executor(program); + executor.execute(std::move(inputStrategy)); + + } 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/Lab3CPPClass/CMakeLists.txt b/Lab3CPPClass/CMakeLists.txt new file mode 100644 index 0000000000..3002708f94 --- /dev/null +++ b/Lab3CPPClass/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(Lab3CPPClass lab3.cpp) +target_include_directories(Lab3CPPClass PUBLIC ../LibraryCPPClass) +target_link_libraries(Lab3CPPClass LibraryCPPClass) + +add_subdirectory(Tests) \ No newline at end of file diff --git a/Lab3CPPClass/Tests/CMakeLists.txt b/Lab3CPPClass/Tests/CMakeLists.txt new file mode 100644 index 0000000000..3e256a620f --- /dev/null +++ b/Lab3CPPClass/Tests/CMakeLists.txt @@ -0,0 +1,49 @@ +add_test( + NAME Lab3CPPClass_Test_1 + COMMAND ${CMAKE_COMMAND} + -DINPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test1.txt + -DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test1-expected.txt + -DTESTNAME=test1 + -DPROGRAM=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_test.cmake +) + +add_test( + NAME Lab3CPPClass_Test_2 + COMMAND ${CMAKE_COMMAND} + -DINPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test2.txt + -DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test2-expected.txt + -DTESTNAME=test2 + -DPROGRAM=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_test.cmake +) + +add_test( + NAME Lab3CPPClass_Test_3 + COMMAND ${CMAKE_COMMAND} + -DINPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test3.txt + -DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test3-expected.txt + -DTESTNAME=test3 + -DPROGRAM=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_test.cmake +) + +add_test( + NAME Lab3CPPClass_Test_4 + COMMAND ${CMAKE_COMMAND} + -DINPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test4.txt + -DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test4-expected.txt + -DTESTNAME=test4 + -DPROGRAM=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_test.cmake +) + +add_test( + NAME Lab3CPPClass_Test_5 + COMMAND ${CMAKE_COMMAND} + -DINPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test5.txt + -DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test5-expected.txt + -DTESTNAME=test5 + -DPROGRAM=$ + -P ${CMAKE_CURRENT_SOURCE_DIR}/run_test.cmake +) \ No newline at end of file diff --git a/Lab3CPPClass/Tests/run_test.cmake b/Lab3CPPClass/Tests/run_test.cmake new file mode 100644 index 0000000000..c6eda5aeed --- /dev/null +++ b/Lab3CPPClass/Tests/run_test.cmake @@ -0,0 +1,18 @@ +execute_process( + COMMAND ${PROGRAM} ${INPUT_FILE} + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}-actual.txt + RESULT_VARIABLE res +) +if(NOT res EQUAL 0) + message(FATAL_ERROR "Program exited with ${res}") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files + ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}-actual.txt + ${OUTPUT_FILE} + RESULT_VARIABLE diff_res +) +if(NOT diff_res EQUAL 0) + message(FATAL_ERROR "Output differs from expected") +endif() diff --git a/Lab3CPPClass/Tests/test1-expected.txt b/Lab3CPPClass/Tests/test1-expected.txt new file mode 100644 index 0000000000..d2cf79a205 --- /dev/null +++ b/Lab3CPPClass/Tests/test1-expected.txt @@ -0,0 +1,7 @@ + / \ / \ / \ +| S | # | | + \ / \ / \ / \ + | x | # | # | + \ / \ / \ / \ + | x | x | E | + \ / \ / \ / diff --git a/Lab3CPPClass/Tests/test1.txt b/Lab3CPPClass/Tests/test1.txt new file mode 100644 index 0000000000..ad1448de86 --- /dev/null +++ b/Lab3CPPClass/Tests/test1.txt @@ -0,0 +1,3 @@ +S#. +.## +..E \ No newline at end of file diff --git a/Lab3CPPClass/Tests/test2-expected.txt b/Lab3CPPClass/Tests/test2-expected.txt new file mode 100644 index 0000000000..7ada944f61 --- /dev/null +++ b/Lab3CPPClass/Tests/test2-expected.txt @@ -0,0 +1 @@ +No path found from S to E diff --git a/Lab3CPPClass/Tests/test2.txt b/Lab3CPPClass/Tests/test2.txt new file mode 100644 index 0000000000..ae420755dc --- /dev/null +++ b/Lab3CPPClass/Tests/test2.txt @@ -0,0 +1,3 @@ +S## +### +##E diff --git a/Lab3CPPClass/Tests/test3-expected.txt b/Lab3CPPClass/Tests/test3-expected.txt new file mode 100644 index 0000000000..a1ab71ea43 --- /dev/null +++ b/Lab3CPPClass/Tests/test3-expected.txt @@ -0,0 +1,9 @@ + / \ / \ / \ / \ +| S | x | # | | + \ / \ / \ / \ / \ + | # | x | # | | + \ / \ / \ / \ / \ + | | x | # | | + \ / \ / \ / \ / \ + | | x | x | E | + \ / \ / \ / \ / diff --git a/Lab3CPPClass/Tests/test3.txt b/Lab3CPPClass/Tests/test3.txt new file mode 100644 index 0000000000..d818d5e26e --- /dev/null +++ b/Lab3CPPClass/Tests/test3.txt @@ -0,0 +1,4 @@ +S.#. +#.#. +..#. +...E diff --git a/Lab3CPPClass/Tests/test4-expected.txt b/Lab3CPPClass/Tests/test4-expected.txt new file mode 100644 index 0000000000..7ada944f61 --- /dev/null +++ b/Lab3CPPClass/Tests/test4-expected.txt @@ -0,0 +1 @@ +No path found from S to E diff --git a/Lab3CPPClass/Tests/test4.txt b/Lab3CPPClass/Tests/test4.txt new file mode 100644 index 0000000000..5c241b25af --- /dev/null +++ b/Lab3CPPClass/Tests/test4.txt @@ -0,0 +1,4 @@ +S.#. +##.. +.... +...E diff --git a/Lab3CPPClass/Tests/test5-expected.txt b/Lab3CPPClass/Tests/test5-expected.txt new file mode 100644 index 0000000000..0be2f8b43d --- /dev/null +++ b/Lab3CPPClass/Tests/test5-expected.txt @@ -0,0 +1,7 @@ + / \ / \ +| S | x | + \ / \ / \ + | # | x | + \ / \ / \ + | | E | + \ / \ / diff --git a/Lab3CPPClass/Tests/test5.txt b/Lab3CPPClass/Tests/test5.txt new file mode 100644 index 0000000000..3509ff1230 --- /dev/null +++ b/Lab3CPPClass/Tests/test5.txt @@ -0,0 +1,3 @@ +S. +#. +.E diff --git a/Lab3CPPClass/lab3.cpp b/Lab3CPPClass/lab3.cpp new file mode 100644 index 0000000000..c0f2093209 --- /dev/null +++ b/Lab3CPPClass/lab3.cpp @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include "queue.h" +#include + +using namespace std; + +struct Position { + int row; + int col; + + Position(int r = 0, int c = 0) : row(r), col(c) {} + + bool operator==(const Position& other) const { + return row == other.row && col == other.col; + } +}; + +class PositionEncoder { +public: + static int encode(const Position& pos, int width) { + return pos.row * width + pos.col; + } + + static Position decode(int encoded, int width) { + return Position(encoded / width, encoded % width); + } +}; + +class INeighborStrategy { +public: + virtual ~INeighborStrategy() = default; + virtual vector getNeighbors(const Position& pos, + int height, + int width) const = 0; +}; + +class HexagonalNeighborStrategy : public INeighborStrategy { +public: + vector getNeighbors(const Position& pos, + int height, + int width) const override { + vector neighbors; + int row = pos.row; + int col = pos.col; + + if (col > 0) { + neighbors.push_back(Position(row, col - 1)); + } + if (col < width - 1) { + neighbors.push_back(Position(row, col + 1)); + } + + if (row > 0) { + neighbors.push_back(Position(row - 1, col)); + } + if (row < height - 1) { + neighbors.push_back(Position(row + 1, col)); + } + + return neighbors; + } +}; + +class HexagonalMaze { +private: + vector grid_; + int height_; + int width_; + Position start_; + Position end_; + +public: + HexagonalMaze(const vector& grid) + : grid_(grid), + height_(static_cast(grid.size())), + width_(grid.empty() ? 0 : static_cast(grid[0].size())) { + findStartAndEnd(); + } + + int getHeight() const { return height_; } + int getWidth() const { return width_; } + const Position& getStart() const { return start_; } + const Position& getEnd() const { return end_; } + + bool isWalkable(const Position& pos) const { + if (!isInBounds(pos)) { + return false; + } + char cell = grid_[pos.row][pos.col]; + return cell != '#'; + } + + bool isInBounds(const Position& pos) const { + return pos.row >= 0 && pos.row < height_ && + pos.col >= 0 && pos.col < width_; + } + + char getCell(const Position& pos) const { + return grid_[pos.row][pos.col]; + } + +private: + void findStartAndEnd() { + bool foundStart = false; + bool foundEnd = false; + + for (int row = 0; row < height_; ++row) { + for (int col = 0; col < width_; ++col) { + if (grid_[row][col] == 'S') { + start_ = Position(row, col); + foundStart = true; + } else if (grid_[row][col] == 'E') { + end_ = Position(row, col); + foundEnd = true; + } + } + } + + if (!foundStart || !foundEnd) { + throw runtime_error("Maze must contain both S and E"); + } + } +}; + +struct PathFindingResult { + bool pathFound; + vector path; + + PathFindingResult() : pathFound(false) {} +}; + +class HexagonalPathFinder { +private: + const HexagonalMaze& maze_; + unique_ptr neighborStrategy_; + +public: + HexagonalPathFinder(const HexagonalMaze& maze, + unique_ptr strategy) + : maze_(maze), neighborStrategy_(move(strategy)) {} + + PathFindingResult findShortestPath() { + PathFindingResult result; + + int height = maze_.getHeight(); + int width = maze_.getWidth(); + Position start = maze_.getStart(); + Position end = maze_.getEnd(); + + vector> visited(height, vector(width, false)); + + vector> parent( + height, vector(width, Position(-1, -1)) + ); + + Queue queue; + queue.insert(PositionEncoder::encode(start, width)); + visited[start.row][start.col] = true; + + while (!queue.empty()) { + int encoded = queue.get(); + queue.remove(); + + Position current = PositionEncoder::decode(encoded, width); + + if (current == end) { + result.pathFound = true; + result.path = reconstructPath(parent, start, end); + return result; + } + + vector neighbors = + neighborStrategy_->getNeighbors(current, height, width); + + for (const Position& neighbor : neighbors) { + if (maze_.isWalkable(neighbor) && !visited[neighbor.row][neighbor.col]) { + visited[neighbor.row][neighbor.col] = true; + parent[neighbor.row][neighbor.col] = current; + queue.insert(PositionEncoder::encode(neighbor, width)); + } + } + } + + return result; + } + +private: + vector reconstructPath( + const vector>& parent, + const Position& start, + const Position& end) { + + vector path; + Position current = end; + + while (!(current == start)) { + path.push_back(current); + current = parent[current.row][current.col]; + } + path.push_back(start); + + reverse(path.begin(), path.end()); + return path; + } +}; + +class MazeOutputFormatter { +private: + const HexagonalMaze& maze_; + +public: + explicit MazeOutputFormatter(const HexagonalMaze& maze) : maze_(maze) {} + + void printMazeWithPath(const vector& path) const { + int height = maze_.getHeight(); + int width = maze_.getWidth(); + + vector> isOnPath(height, vector(width, false)); + + int pathSize = static_cast(path.size()); + for (int i = 1; i < pathSize - 1; ++i) { + isOnPath[path[i].row][path[i].col] = true; + } + + for (int row = 0; row < height; ++row) { + if (row == 0) { + cout << " "; + for (int col = 0; col < width; ++col) { + cout << "/ \\ "; + } + cout << "\n"; + } + + int contentIndent = (row == 0) ? 0 : row * 2; + for (int i = 0; i < contentIndent; ++i) { + cout << " "; + } + + cout << "|"; + for (int col = 0; col < width; ++col) { + char cell = maze_.getCell(Position(row, col)); + + if (cell == 'S' || cell == 'E') { + cout << " " << cell << " |"; + } else if (isOnPath[row][col]) { + cout << " x |"; + } else if (cell == '#') { + cout << " # |"; + } else { + cout << " |"; + } + } + cout << "\n"; + + int borderIndent = row * 2 + 1; + for (int i = 0; i < borderIndent; ++i) { + cout << " "; + } + + for (int col = 0; col < width; ++col) { + cout << "\\ / "; + } + + if (row < height - 1) { + cout << "\\"; + } + + cout << "\n"; + } + } +}; + +class MazeFileReader { +public: + static vector readMaze(ifstream& input) { + vector grid; + string line; + + while (getline(input, line)) { + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + if (!line.empty()) { + grid.push_back(line); + } + } + + if (grid.empty()) { + throw runtime_error("Empty maze file"); + } + + return grid; + } +}; + +class MazeSolverApplication { +public: + int run(ifstream& input) { + try { + static vector grid = MazeFileReader::readMaze(input); + + HexagonalMaze maze(grid); + + HexagonalPathFinder pathFinder( + maze, + make_unique() + ); + + PathFindingResult result = pathFinder.findShortestPath(); + + if (result.pathFound) { + MazeOutputFormatter formatter(maze); + formatter.printMazeWithPath(result.path); + } else { + cout << "No path found from S to E\n"; + } + + return 0; + + } catch (const exception& e) { + cerr << "Error: " << e.what() << endl; + return 1; + } + } +}; + +int main(int argc, char* argv[]) { + if (argc < 2) { + cerr << "Usage: " << argv[0] << " " << endl; + return 1; + } + + ifstream input(argv[1]); + if (!input) { + cerr << "Cannot open file: " << argv[1] << endl; + return 1; + } + + MazeSolverApplication app; + int result = app.run(input); + + input.close(); + return result; +} \ No newline at end of file diff --git a/Lab4CPPTemplate/CMakeLists.txt b/Lab4CPPTemplate/CMakeLists.txt new file mode 100644 index 0000000000..081ff21bc1 --- /dev/null +++ b/Lab4CPPTemplate/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(Lab4CPPTemplate lab4.cpp) +target_include_directories(Lab4CPPTemplate PUBLIC ../LibraryCPPTemplate) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/input.txt input.txt COPYONLY) + +add_test(NAME KruskalTest COMMAND Lab4CPPTemplate input.txt) + +set_tests_properties(KruskalTest PROPERTIES PASS_REGULAR_EXPRESSION "MST Weight: 19") \ No newline at end of file diff --git a/Lab4CPPTemplate/input.txt b/Lab4CPPTemplate/input.txt new file mode 100644 index 0000000000..8557c9bbec --- /dev/null +++ b/Lab4CPPTemplate/input.txt @@ -0,0 +1,10 @@ +4 5 +A +B +C +D +A B 10 +A C 6 +A D 5 +B D 15 +C D 4 diff --git a/Lab4CPPTemplate/lab4.cpp b/Lab4CPPTemplate/lab4.cpp new file mode 100644 index 0000000000..23c1538094 --- /dev/null +++ b/Lab4CPPTemplate/lab4.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include "graph.h" +#include "vector.h" + +struct EdgeInfo { + size_t u; + size_t v; + int weight; +}; + +struct DSU { + Vector parent; + Vector rank; + + DSU(size_t n) { + parent.resize(n); + rank.resize(n); + for (size_t i = 0; i < n; ++i) { + parent[i] = i; + rank[i] = 0; + } + } + + size_t find_set(size_t v) { + if (v == parent[v]) + return v; + size_t root = find_set(parent[v]); + parent[v] = root; + return root; + } + + bool union_sets(size_t a, size_t b) { + a = find_set(a); + b = find_set(b); + if (a != b) { + if (rank[a] < rank[b]) + std::swap(a, b); + parent[b] = a; + if (rank[a] == rank[b]) + rank[a]++; + return true; + } + return false; + } +}; + +void sort_edges(Vector& edges, size_t count) { + if (count == 0) return; + for (size_t i = 0; i < count - 1; ++i) { + for (size_t j = 0; j < count - i - 1; ++j) { + if (edges[j].weight > edges[j + 1].weight) { + EdgeInfo temp = edges[j]; + edges[j] = edges[j + 1]; + edges[j + 1] = temp; + } + } + } +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " \n"; + return 1; + } + + std::ifstream file(argv[1]); + if (!file.is_open()) { + std::cout << "Error opening file\n"; + return 1; + } + + size_t n; + int m; + if (!(file >> n >> m)) { + std::cout << "Error reading n and m\n"; + return 1; + } + + Graph graph(n); + Vector names; + names.resize(n); + + for (size_t i = 0; i < n; ++i) { + std::string name; + file >> name; + graph.set_vertex_label(i, name); + names[i] = name; + } + + const size_t NOT_FOUND = std::numeric_limits::max(); + + for (int i = 0; i < m; ++i) { + std::string u_name, v_name; + int w; + file >> u_name >> v_name >> w; + + size_t u = NOT_FOUND; + size_t v = NOT_FOUND; + + for (size_t k = 0; k < n; ++k) { + if (names[k] == u_name) u = k; + if (names[k] == v_name) v = k; + } + + if (u != NOT_FOUND && v != NOT_FOUND) { + graph.add_edge(u, v, w); + graph.add_edge(v, u, w); + } + } + + Vector all_edges; + all_edges.resize(m); + size_t edge_count = 0; + + for (size_t i = 0; i < graph.vertex_count(); ++i) { + auto it = graph.get_neighbor_iterator(i); + while (it.has_next()) { + size_t neighbor = it.next(); + + if (i < neighbor) { + EdgeInfo e; + e.u = i; + e.v = neighbor; + e.weight = graph.get_edge_label(i, neighbor); + + all_edges[edge_count] = e; + edge_count++; + } + } + } + + sort_edges(all_edges, edge_count); + + DSU dsu(n); + long long mst_weight = 0; + Vector mst_result; + mst_result.resize(n - 1); + size_t mst_count = 0; + + for (size_t i = 0; i < edge_count; ++i) { + EdgeInfo e = all_edges[i]; + if (dsu.union_sets(e.u, e.v)) { + mst_weight += e.weight; + + if (mst_count < mst_result.size()) { + mst_result[mst_count] = e; + mst_count++; + } + } + } + + std::cout << "MST Weight: " << mst_weight << "\n"; + std::cout << "Edges:\n"; + for (size_t i = 0; i < mst_count; ++i) { + EdgeInfo e = mst_result[i]; + std::cout << graph.get_vertex_label(e.u) << " - " + << graph.get_vertex_label(e.v) << " (" + << e.weight << ")\n"; + } + + return 0; +} diff --git a/Lab5CPPClass/CMakeLists.txt b/Lab5CPPClass/CMakeLists.txt new file mode 100644 index 0000000000..9782481b76 --- /dev/null +++ b/Lab5CPPClass/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(Lab5CPPClass lab5.cpp) +target_include_directories(Lab5CPPClass PUBLIC ../LibraryCPPClass) +target_link_libraries(Lab5CPPClass LibraryCPPClass) + +add_test(NAME Lab5_Benchmark_Run COMMAND Lab5CPPClass) \ No newline at end of file diff --git a/Lab5CPPClass/lab5.cpp b/Lab5CPPClass/lab5.cpp new file mode 100644 index 0000000000..0d5b5f67ac --- /dev/null +++ b/Lab5CPPClass/lab5.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "splay_tree.h" + +// генератор входных данных +std::string random_string(size_t length) { + static const char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::string s; + s.reserve(length); + for (size_t i = 0; i < length; ++i) { + s += alphanum[rand() % (static_cast(sizeof(alphanum) - 1))]; + } + return s; +} + +std::vector generate_data(size_t count) { + std::vector data; + data.reserve(count); + for (size_t i = 0; i < count; ++i) { + data.push_back(random_string(10)); + } + return data; +} + +// Вспомогательная функция для рисования полоски в файл +void write_bar(std::ofstream& out, double val, double max_val, int width = 50) { + if (max_val == 0) max_val = 1; + int len = static_cast((val / max_val) * width); + out << "["; + for (int i = 0; i < len; ++i) out << "#"; + for (int i = len; i < width; ++i) out << " "; + out << "] " << std::fixed << std::setprecision(4) << val << "s"; +} + +int main() { + srand(static_cast(time(0))); + + std::ofstream result_file("lab5_results.txt"); + if (!result_file.is_open()) { + std::cerr << "Error: Could not open lab5_results.txt for writing.\n"; + return 1; + } + + std::vector sizes = {10000, 50000, 100000, 500000, 1000000}; + double max_time = 0.0; + + struct Result { + size_t size; + double splay_time; + double set_time; + }; + std::vector results; + + std::cout << "Running benchmarks (Please wait)...\n"; + + // Замеры + for (size_t n : sizes) { + std::cout << "Processing N = " << n << "...\n"; + std::vector data = generate_data(n); + + // 1. Тест Splay Tree + double splay_dur = 0; + { + SplayTree st; + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& s : data) st.insert(s); + auto end = std::chrono::high_resolution_clock::now(); + splay_dur = std::chrono::duration(end - start).count(); + } + + // 2. Тест std::set + double set_dur = 0; + { + std::set st; + auto start = std::chrono::high_resolution_clock::now(); + for (const auto& s : data) st.insert(s); + auto end = std::chrono::high_resolution_clock::now(); + set_dur = std::chrono::duration(end - start).count(); + } + + results.push_back({n, splay_dur, set_dur}); + + if (splay_dur > max_time) max_time = splay_dur; + if (set_dur > max_time) max_time = set_dur; + } + + // Запись графика в файл + result_file << "=== Performance Graph (Insertion Time vs Data Size) ===\n\n"; + result_file << "Method | Graph (Visualization)\n"; + result_file << "----------------------------------------------------------------\n"; + + for (const auto& res : results) { + result_file << "\nData Size: " << res.size << "\n"; + + result_file << "Splay Tree : "; + write_bar(result_file, res.splay_time, max_time); + result_file << "\n"; + + result_file << "std::set : "; + write_bar(result_file, res.set_time, max_time); + result_file << "\n"; + } + + result_file << "\nAnalysis:\n"; + result_file << "std::set (Red-Black Tree) usually performs better on random data inserts\n"; + result_file << "due to lower overhead compared to Splay Tree rotations.\n"; + + result_file.close(); + std::cout << "Done! Results saved to 'lab5_results.txt'.\n"; + + return 0; +} diff --git a/Lab5CPPClass/lab5_results_example.txt b/Lab5CPPClass/lab5_results_example.txt new file mode 100644 index 0000000000..d65b088b82 --- /dev/null +++ b/Lab5CPPClass/lab5_results_example.txt @@ -0,0 +1,28 @@ +=== Performance Graph (Insertion Time vs Data Size) === + +Method | Graph (Visualization) +---------------------------------------------------------------- + +Data Size: 10000 +Splay Tree : [ ] 0.0114s +std::set : [ ] 0.0126s + +Data Size: 50000 +Splay Tree : [# ] 0.0709s +std::set : [# ] 0.0800s + +Data Size: 100000 +Splay Tree : [### ] 0.1533s +std::set : [### ] 0.1393s + +Data Size: 500000 +Splay Tree : [###################### ] 0.9991s +std::set : [################## ] 0.8371s + +Data Size: 1000000 +Splay Tree : [##################################################] 2.2471s +std::set : [######################################### ] 1.8481s + +Analysis: +std::set (Red-Black Tree) usually performs better on random data inserts +due to lower overhead compared to Splay Tree rotations. diff --git a/LibraryC/Tests/CMakeLists.txt b/LibraryC/Tests/CMakeLists.txt index b182dee615..170e33664b 100644 --- a/LibraryC/Tests/CMakeLists.txt +++ b/LibraryC/Tests/CMakeLists.txt @@ -1,7 +1,7 @@ -add_executable(TestArrayC array.cpp) -target_include_directories(TestArrayC PUBLIC ..) -target_link_libraries(TestArrayC LibraryC) -add_test(TestArrayC TestArrayC) +# add_executable(TestArrayC array.cpp) +# target_include_directories(TestArrayC PUBLIC ..) +# target_link_libraries(TestArrayC LibraryC) +# add_test(TestArrayC TestArrayC) # add_executable(TestListC list.cpp) # target_include_directories(TestListC PUBLIC ..) diff --git a/LibraryCPPClass/CMakeLists.txt b/LibraryCPPClass/CMakeLists.txt index 0ddd51cba2..b419b4440d 100644 --- a/LibraryCPPClass/CMakeLists.txt +++ b/LibraryCPPClass/CMakeLists.txt @@ -1,3 +1,3 @@ -add_library(LibraryCPPClass STATIC array.cpp list.cpp queue.cpp stack.cpp vector.cpp) +add_library(LibraryCPPClass STATIC array.cpp vector.cpp stack.cpp list.cpp queue.cpp splay_tree.cpp) add_subdirectory(Tests) diff --git a/LibraryCPPClass/Tests/CMakeLists.txt b/LibraryCPPClass/Tests/CMakeLists.txt index 748ae652cc..bac4fb2698 100644 --- a/LibraryCPPClass/Tests/CMakeLists.txt +++ b/LibraryCPPClass/Tests/CMakeLists.txt @@ -1,26 +1,32 @@ -# add_executable(TestArrayCPPClass array.cpp) -# target_include_directories(TestArrayCPPClass PUBLIC ..) -# target_link_libraries(TestArrayCPPClass LibraryCPPClass) -# add_test(TestArrayCPPClass TestArrayCPPClass) +add_executable(TestArrayCPPClass array.cpp) +target_include_directories(TestArrayCPPClass PUBLIC ..) +target_link_libraries(TestArrayCPPClass LibraryCPPClass) +add_test(TestArrayCPPClass TestArrayCPPClass) -# add_executable(TestListCPPClass list.cpp) -# target_include_directories(TestListCPPClass PUBLIC ..) -# target_link_libraries(TestListCPPClass LibraryCPPClass) -# add_test(TestListCPPClass TestListCPPClass) +add_executable(TestListCPPClass list.cpp) +target_include_directories(TestListCPPClass PUBLIC ..) +target_link_libraries(TestListCPPClass LibraryCPPClass) +add_test(TestListCPPClass TestListCPPClass) -# add_executable(TestQueueCPPClass queue.cpp) -# target_include_directories(TestQueueCPPClass PUBLIC ..) -# target_link_libraries(TestQueueCPPClass LibraryCPPClass) -# add_test(TestQueueCPPClass TestQueueCPPClass) -# set_tests_properties(TestQueueCPPClass PROPERTIES TIMEOUT 10) +add_executable(TestQueueCPPClass queue.cpp) +target_include_directories(TestQueueCPPClass PUBLIC ..) +target_link_libraries(TestQueueCPPClass LibraryCPPClass) +add_test(TestQueueCPPClass TestQueueCPPClass) +set_tests_properties(TestQueueCPPClass PROPERTIES TIMEOUT 10) -# add_executable(TestStackCPPClass stack.cpp) -# target_include_directories(TestStackCPPClass PUBLIC ..) -# target_link_libraries(TestStackCPPClass LibraryCPPClass) -# add_test(TestStackCPPClass TestStackCPPClass) +add_executable(TestStackCPPClass stack.cpp) +target_include_directories(TestStackCPPClass PUBLIC ..) +target_link_libraries(TestStackCPPClass LibraryCPPClass) +add_test(TestStackCPPClass TestStackCPPClass) -# add_executable(TestVectorCPPClass vector.cpp) -# target_include_directories(TestVectorCPPClass PUBLIC ..) -# target_link_libraries(TestVectorCPPClass LibraryCPPClass) -# add_test(TestVectorCPPClass TestVectorCPPClass) -# set_tests_properties(TestVectorCPPClass PROPERTIES TIMEOUT 10) +add_executable(TestVectorCPPClass vector.cpp) +target_include_directories(TestVectorCPPClass PUBLIC ..) +target_link_libraries(TestVectorCPPClass LibraryCPPClass) +add_test(TestVectorCPPClass TestVectorCPPClass) +set_tests_properties(TestVectorCPPClass PROPERTIES TIMEOUT 10) + +add_executable(TestSplayContainerCPPClass splay_tree.cpp) +target_include_directories(TestSplayContainerCPPClass PUBLIC ..) +target_link_libraries(TestSplayContainerCPPClass LibraryCPPClass) +add_test(TestSplayContainerCPPClass TestSplayContainerCPPClass) +set_tests_properties(TestSplayContainerCPPClass PROPERTIES TIMEOUT 10) \ No newline at end of file diff --git a/LibraryCPPClass/Tests/splay_tree.cpp b/LibraryCPPClass/Tests/splay_tree.cpp new file mode 100644 index 0000000000..e06ebf9253 --- /dev/null +++ b/LibraryCPPClass/Tests/splay_tree.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include "splay_tree.h" + +// Проверка базовой логики: добавление, поиск, удаление +void test_correctness() { + SplayTree st; + + // 1. Вставка + st.insert("apple"); + st.insert("banana"); + st.insert("cherry"); + + // 2. Поиск + if (st.contains("apple") && st.contains("banana") && st.contains("cherry")) { + std::cout << "[OK] Insert and Find existing elements passed.\n"; + } else { + std::cerr << "[FAIL] Insert or Find broken.\n"; + exit(1); + } + + if (!st.contains("dragonfruit")) { + std::cout << "[OK] Find non-existing element passed.\n"; + } else { + std::cerr << "[FAIL] Found element that shouldn't exist.\n"; + exit(1); + } + + // 3. Удаление + st.remove("banana"); + if (!st.contains("banana") && st.contains("apple")) { + std::cout << "[OK] Remove passed.\n"; + } else { + std::cerr << "[FAIL] Remove broken.\n"; + exit(1); + } +} + +// Нагрузочное тестирование +void test_performance_sorted() { + SplayTree st; + const int N = 1000000; + + std::vector data; + data.reserve(N); + for (int i = 0; i < N; ++i) { + data.push_back(std::to_string(i)); + } + + auto start = std::chrono::high_resolution_clock::now(); + + for (const auto& s : data) { + st.insert(s); + } + + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = end - start; + + std::cout << "[INFO] Sorted insert of " << N << " elements took " << diff.count() << "s.\n"; + + if (diff.count() > 2.5) { + std::cerr << "[FAIL] Performance test failed! Possible O(N^2) complexity detected.\n"; + exit(1); + } else { + std::cout << "[OK] Performance test passed (Linear complexity confirmed).\n"; + } +} + +int main() { + test_correctness(); + test_performance_sorted(); + return 0; +} \ No newline at end of file diff --git a/LibraryCPPClass/Tests/vector.cpp b/LibraryCPPClass/Tests/vector.cpp index dd410ab052..0447e620ce 100644 --- a/LibraryCPPClass/Tests/vector.cpp +++ b/LibraryCPPClass/Tests/vector.cpp @@ -13,7 +13,7 @@ int main() } for (size_t i = 0 ; i < vector.size() ; ++i) - vector.set(i, i); + vector.set(i, (Data)i); vector = vector; diff --git a/LibraryCPPClass/array.cpp b/LibraryCPPClass/array.cpp index 5f9679d07f..8b485cd873 100644 --- a/LibraryCPPClass/array.cpp +++ b/LibraryCPPClass/array.cpp @@ -1,32 +1,67 @@ #include "array.h" +#include -Array::Array(size_t size) + +Array::Array(size_t size) : size_(size), data_(new Data[size]) { + if (data_ == nullptr) + { + throw std::bad_alloc(); + } } -Array::Array(const Array &a) +Array::Array(const Array &a) :size_(a.size_), data_(new Data[size_]) { + for (size_t i = 0; i < size_; i++) + { + data_[i] = a.data_[i]; + } } Array &Array::operator=(const Array &a) { + if (&a != this) + { + delete[] data_; + size_ = a.size_; + data_ = new Data[size_]; + + for (size_t i = 0; i < size_; i++) + { + data_[i] = a.data_[i]; + } + } return *this; } Array::~Array() { + delete[] data_; } Data Array::get(size_t index) const { - return Data(0); + if (index >= size_) { + throw std::out_of_range("Invalid array size"); + } + else + { + return data_[index]; + } } -void Array::set(size_t index, Data value) -{ +void Array::set(size_t index, Data value) { + if(index >= size_) + { + throw std::out_of_range("Invalid array size"); + } + else + { + data_[index] = value; + } } size_t Array::size() const { - return 0; + return size_; } diff --git a/LibraryCPPClass/array.h b/LibraryCPPClass/array.h index 7ebd54f7a1..913273db4f 100644 --- a/LibraryCPPClass/array.h +++ b/LibraryCPPClass/array.h @@ -8,15 +8,17 @@ typedef int Data; class Array { + + public: // create array explicit Array(size_t size); // copy constructor - Array(const Array &a); + Array(const Array& a); // assignment operator - Array &operator=(const Array &a); + Array& operator=(const Array& a); // delete array, free memory ~Array(); @@ -32,6 +34,9 @@ class Array private: // private data should be here + size_t size_; + Data* data_; + }; #endif diff --git a/LibraryCPPClass/list.cpp b/LibraryCPPClass/list.cpp index a08e44fad8..98aff1521e 100644 --- a/LibraryCPPClass/list.cpp +++ b/LibraryCPPClass/list.cpp @@ -1,44 +1,165 @@ -#include #include "list.h" -List::List() -{ -} +List::List() = default; List::List(const List &a) { + copy(a); } List &List::operator=(const List &a) { + if (this != &a) { + clear(); + copy(a); + } return *this; } List::~List() { + clear(); } List::Item *List::first() { - return nullptr; + return _head; +} + +List::Item *List::last() { + return _tail; +} + +const List::Item *List::last() const { + return _tail; } List::Item *List::insert(Data data) { - return nullptr; + Item *new_item = new Item(data); + new_item->_next = _head; + _head = new_item; + + if (!_tail) { + _tail = new_item; + } + + return new_item; +} + +List::Item *List::insert_end(Data data) { + Item *new_item = new Item(data); + + if (_tail) { + _tail->_next = new_item; + _tail = new_item; + } else { + _head = _tail = new_item; + } + + return new_item; } List::Item *List::insert_after(Item *item, Data data) { - return nullptr; + if (!item) { + return insert(data); + } + + Item *new_item = new Item(data); + new_item->_next = item->_next; + item->_next = new_item; + + if (item == _tail) { + _tail = new_item; + } + + return new_item; +} + +List::Item *List::erase(Item *item) { + if (!item) return nullptr; + + if (item == _head) { + return erase_first(); + } + + Item* prev = _head; + while (prev && prev->_next != item) { + prev = prev->_next; + } + + if (!prev) return nullptr; + + prev->_next = item->_next; + + if (item == _tail) { + _tail = prev; + } + + Item* result = item->_next; + delete item; + return result; } List::Item *List::erase_first() { - return nullptr; + return erase_next(nullptr); } List::Item *List::erase_next(Item *item) { - return nullptr; + if (item == nullptr) + { + if (!_head) return nullptr; + + Item* to_delete = _head; + _head = _head->_next; + + if (!_head) { + _tail = nullptr; + } + + Item* result = _head; + delete to_delete; + return result; + } else { + if (!item->_next) return nullptr; + + Item* to_delete = item->_next; + item->_next = to_delete->_next; + + if (to_delete == _tail) { + _tail = item; + } + + Item* result = item->_next; + delete to_delete; + return result; + } +} + +void List::copy(const List &a) { + if (!a._head) { + _head = _tail = nullptr; + return; + } + + _head = new Item(a._head->_data); + Item *current = _head; + Item *src = a._head->_next; + + while (src) { + current->_next = new Item(src->_data); + current = current->_next; + src = src->_next; + } + + _tail = current; +} + +void List::clear() { + while (_head) { + erase_first(); + } } diff --git a/LibraryCPPClass/list.h b/LibraryCPPClass/list.h index 67da6907f7..f2e87db320 100644 --- a/LibraryCPPClass/list.h +++ b/LibraryCPPClass/list.h @@ -1,8 +1,6 @@ #ifndef LIST_H #define LIST_H -#include - // Change it to desired type typedef int Data; @@ -12,35 +10,49 @@ class List class Item { public: - Item *next() { return nullptr; } - Item *prev() { return nullptr; } - Data data() const { return Data(); } + Item *next() { return _next; } + Data data() const { return _data; } + private: - // internal data here + Item* _next; + Data _data; + Item(Data d): _next(nullptr), _data(d) {} + friend class List; }; // Creates new list List(); - + // copy constructor List(const List &a); - + // assignment operator List &operator=(const List &a); - + // Destroys the list and frees the memory ~List(); + void clear(); + void copy(const List &a); + // Retrieves the first item from the list Item *first(); + Item *last(); + const Item *last() const; // Inserts new list item into the beginning Item *insert(Data data); + Item *insert_end(Data data); // Inserts new list item after the specified item // Inserts first element if item is null Item *insert_after(Item *item, Data data); + // Erases the specified item + // Returns pointer to the item next to the deleted one + // O(n) complexity for singly-linked list + Item *erase(Item *item); + // Deletes the first list item. // Returns pointer to the item next to the deleted one. Item *erase_first(); @@ -48,10 +60,12 @@ class List // Deletes the list item following the specified one. // Deletes the first element when item is null. // Returns pointer to the item next to the deleted one. - // Should be O(1) + // O(1) Item *erase_next(Item *item); + private: - // private data should be here + Item* _head = nullptr; + Item* _tail = nullptr; }; #endif diff --git a/LibraryCPPClass/queue.cpp b/LibraryCPPClass/queue.cpp index 395c05b7f2..74d7088af9 100644 --- a/LibraryCPPClass/queue.cpp +++ b/LibraryCPPClass/queue.cpp @@ -1,38 +1,46 @@ #include "queue.h" +#include +#include "list.h" -Queue::Queue() -{ -} +Queue::Queue(): _list(new List()) {}; -Queue::Queue(const Queue &a) -{ - // implement or disable this function -} +Queue::Queue(const Queue &a): _list(new List(*a._list)) {} Queue &Queue::operator=(const Queue &a) { - // implement or disable this function + if (this != &a) { + _list = a._list; + } return *this; } -Queue::~Queue() -{ -} +Queue::~Queue() { + delete _list; +}; void Queue::insert(Data data) { + _list -> insert_end(data); } Data Queue::get() const { - return Data(); + if (empty()) + { + throw std::runtime_error("Queue is empty"); + } + return _list -> first()->data(); } void Queue::remove() { + if (empty()) { + throw std::runtime_error("Queue is empty"); + } + _list -> erase_first(); } bool Queue::empty() const { - return true; + return _list -> first() == nullptr; } diff --git a/LibraryCPPClass/queue.h b/LibraryCPPClass/queue.h index b221979aae..1247bd7965 100644 --- a/LibraryCPPClass/queue.h +++ b/LibraryCPPClass/queue.h @@ -2,6 +2,7 @@ #define QUEUE_H #include +#include "list.h" // Change it to desired type typedef int Data; @@ -36,7 +37,7 @@ class Queue bool empty() const; private: - // private data should be here + List* _list; }; #endif diff --git a/LibraryCPPClass/splay_tree.cpp b/LibraryCPPClass/splay_tree.cpp new file mode 100644 index 0000000000..2d10ec3a42 --- /dev/null +++ b/LibraryCPPClass/splay_tree.cpp @@ -0,0 +1,132 @@ +#include "splay_tree.h" + +SplayTree::SplayTree() : root_(nullptr) {} + +SplayTree::~SplayTree() { + delete_tree(root_); +} + +void SplayTree::delete_tree(Node* node) { + while (node) { + if (node->left) { + Node* left = node->left; + node->left = left->right; + left->right = node; + node = left; + } else { + Node* right = node->right; + delete node; + node = right; + } + } +} + +void SplayTree::rotate_right(Node*& node) { + Node* left_child = node->left; + node->left = left_child->right; + left_child->right = node; + node = left_child; +} + +void SplayTree::rotate_left(Node*& node) { + Node* right_child = node->right; + node->right = right_child->left; + right_child->left = node; + node = right_child; +} + +void SplayTree::splay(Node*& root, const std::string& key) { + if (!root) return; + + Node header(""); + Node* leftTreeMax = &header; + Node* rightTreeMin = &header; + + Node* t = root; + header.left = header.right = nullptr; + + while (true) { + if (key < t->key) { + if (!t->left) break; + if (key < t->left->key) { + // Zig-Zig: Rotate right first + rotate_right(t); + if (!t->left) break; + } + // Link to Right Tree + rightTreeMin->left = t; + rightTreeMin = t; + t = t->left; + } else if (key > t->key) { + if (!t->right) break; + if (key > t->right->key) { + // Zig-Zig: Rotate left first + rotate_left(t); + if (!t->right) break; + } + // Link to Left Tree + leftTreeMax->right = t; + leftTreeMax = t; + t = t->right; + } else { + break; // Found + } + } + + // Assemble + leftTreeMax->right = t->left; + rightTreeMin->left = t->right; + t->left = header.right; + t->right = header.left; + + root = t; +} + +void SplayTree::insert(const std::string& key) { + if (!root_) { + root_ = new Node(key); + return; + } + + splay(root_, key); + + if (root_->key == key) return; // Уже есть + + Node* new_node = new Node(key); + if (key < root_->key) { + new_node->right = root_; + new_node->left = root_->left; + root_->left = nullptr; + } else { + new_node->left = root_; + new_node->right = root_->right; + root_->right = nullptr; + } + root_ = new_node; +} + +bool SplayTree::contains(const std::string& key) { + if (!root_) return false; + splay(root_, key); + return root_->key == key; +} + +void SplayTree::remove(const std::string& key) { + if (!root_) return; + + splay(root_, key); // Если элемент есть, он теперь в корне + + if (root_->key != key) return; + + Node* temp = root_; + if (!root_->left) { + root_ = root_->right; + } else { + // Splay max element in left subtree + Node* left_root = root_->left; + splay(left_root, key); // Ключ key гарантированно больше всех в левом поддереве, поэтому splay поднимет MAX + left_root->right = root_->right; + root_ = left_root; + } + delete temp; +} diff --git a/LibraryCPPClass/splay_tree.h b/LibraryCPPClass/splay_tree.h new file mode 100644 index 0000000000..c3cf8392b3 --- /dev/null +++ b/LibraryCPPClass/splay_tree.h @@ -0,0 +1,35 @@ +#ifndef SPLAY_TREE_H +#define SPLAY_TREE_H + +#include + +class SplayTree { +public: + SplayTree(); + virtual ~SplayTree(); + + // Запрещаем копирование, чтобы избежать неявных тяжелых операций + SplayTree(const SplayTree&) = delete; + SplayTree& operator=(const SplayTree&) = delete; + + void insert(const std::string& key); + bool contains(const std::string& key); + void remove(const std::string& key); + +private: + struct Node { + std::string key; + Node* left = nullptr; + Node* right = nullptr; + Node(const std::string& k) : key(k) {} + }; + + Node* root_; + + void rotate_right(Node*& node); + void rotate_left(Node*& node); + void splay(Node*& root, const std::string& key); + void delete_tree(Node* node); +}; + +#endif diff --git a/LibraryCPPClass/stack.cpp b/LibraryCPPClass/stack.cpp index 0dc91d6c29..e9f0c9bfb2 100644 --- a/LibraryCPPClass/stack.cpp +++ b/LibraryCPPClass/stack.cpp @@ -1,38 +1,46 @@ #include "stack.h" Stack::Stack() -{ -} +{} -Stack::Stack(const Stack &a) -{ - // implement or disable this function -} +Stack::Stack(const Stack &a) : vector_(a.vector_) +{} Stack &Stack::operator=(const Stack &a) { - // implement or disable this function + if (this != &a) { + vector_ = a.vector_; + } return *this; } Stack::~Stack() { + // Vector destructor handles cleanup automatically } void Stack::push(Data data) { + vector_.resize(vector_.size() + 1); + vector_.set(vector_.size() - 1, data); } Data Stack::get() const { - return Data(); + if (vector_.size() == 0) { + return Data(); + } + return vector_.get(vector_.size() - 1); } void Stack::pop() { + if (vector_.size() > 0) { + vector_.resize(vector_.size() - 1); + } } bool Stack::empty() const { - return true; + return vector_.size() == 0; } diff --git a/LibraryCPPClass/stack.h b/LibraryCPPClass/stack.h index 740c4c3e64..64b9be1d19 100644 --- a/LibraryCPPClass/stack.h +++ b/LibraryCPPClass/stack.h @@ -2,6 +2,7 @@ #define STACK_H #include +#include "vector.h" // Change it to desired type typedef int Data; @@ -36,7 +37,7 @@ class Stack bool empty() const; private: - // private data should be here + Vector vector_; }; #endif diff --git a/LibraryCPPClass/vector.cpp b/LibraryCPPClass/vector.cpp index 5bab7f34be..91d4f5a5f9 100644 --- a/LibraryCPPClass/vector.cpp +++ b/LibraryCPPClass/vector.cpp @@ -1,36 +1,90 @@ #include "vector.h" +#include -Vector::Vector() +Vector::Vector() : data_(nullptr), size_(0), capacity_(0) +{} + +void Vector::copy_from(const Vector& a) { + if (a.size_ > 0) { + capacity_ = a.capacity_; + data_ = new Data[capacity_]; + size_ = a.size_; + for (size_t i = 0; i < size_; ++i) { + data_[i] = a.data_[i]; + } + } else { + data_ = nullptr; + size_ = 0; + capacity_ = 0; + } } -Vector::Vector(const Vector &a) +Vector::Vector(const Vector& a) : data_(nullptr), size_(0), capacity_(0) { + copy_from(a); } -Vector &Vector::operator=(const Vector &a) +Vector& Vector::operator=(const Vector& a) { + if (this != &a) { + delete[] data_; + + copy_from(a); + } return *this; } Vector::~Vector() { + delete[] data_; } Data Vector::get(size_t index) const { - return Data(); + if (index >= size_) { + return Data(); + } + return data_[index]; } void Vector::set(size_t index, Data value) { + if (index >= size_) { + return; + } + data_[index] = value; } size_t Vector::size() const { - return 0; + return size_; } -void Vector::resize(size_t size) +void Vector::resize(size_t new_size) { + if (new_size <= capacity_) { + size_ = new_size; + return; + } + + size_t new_capacity = capacity_ == 0 ? 1 : capacity_; + while (new_capacity < new_size) { + new_capacity *= 2; + } + + Data* new_data = new Data[new_capacity]; + + for (size_t i = 0; i < size_; ++i) { + new_data[i] = data_[i]; + } + + for (size_t i = size_; i < new_size; ++i) { + new_data[i] = Data(); + } + + delete[] data_; + data_ = new_data; + capacity_ = new_capacity; + size_ = new_size; } diff --git a/LibraryCPPClass/vector.h b/LibraryCPPClass/vector.h index 1d1d656da6..36d38c41a1 100644 --- a/LibraryCPPClass/vector.h +++ b/LibraryCPPClass/vector.h @@ -13,10 +13,10 @@ class Vector Vector(); // copy constructor - Vector(const Vector &a); + Vector(const Vector& a); // assignment operator - Vector &operator=(const Vector &a); + Vector& operator=(const Vector& a); // Deletes vector structure and internal data ~Vector(); @@ -35,7 +35,11 @@ class Vector void resize(size_t size); private: - // private data should be here + Data* data_; + size_t size_; + size_t capacity_; + + void copy_from(const Vector& a); }; #endif diff --git a/LibraryCPPTemplate/Tests/CMakeLists.txt b/LibraryCPPTemplate/Tests/CMakeLists.txt index cbee1c72d5..7c2414379f 100644 --- a/LibraryCPPTemplate/Tests/CMakeLists.txt +++ b/LibraryCPPTemplate/Tests/CMakeLists.txt @@ -15,7 +15,12 @@ # target_include_directories(TestStackCPPTemplate PUBLIC ..) # add_test(TestStackCPPTemplate TestStackCPPTemplate) -# add_executable(TestVectorCPPTemplate vector.cpp) -# target_include_directories(TestVectorCPPTemplate PUBLIC ..) -# add_test(TestVectorCPPTemplate TestVectorCPPTemplate) -# set_tests_properties(TestVectorCPPTemplate PROPERTIES TIMEOUT 10) +add_executable(TestVectorCPPTemplate vector.cpp) +target_include_directories(TestVectorCPPTemplate PUBLIC ..) +add_test(TestVectorCPPTemplate TestVectorCPPTemplate) +set_tests_properties(TestVectorCPPTemplate PROPERTIES TIMEOUT 10) + +add_executable(TestGraphCPPTemplate graph.cpp) +target_include_directories(TestGraphCPPTemplate PUBLIC ..) +add_test(TestGraphCPPTemplate TestGraphCPPTemplate) +set_tests_properties(TestGraphCPPTemplate PROPERTIES TIMEOUT 10) \ No newline at end of file diff --git a/LibraryCPPTemplate/Tests/graph.cpp b/LibraryCPPTemplate/Tests/graph.cpp new file mode 100644 index 0000000000..f2dc12cc67 --- /dev/null +++ b/LibraryCPPTemplate/Tests/graph.cpp @@ -0,0 +1,116 @@ +#include +#include +#include "graph.h" + +typedef Graph MyGraph; + +int main() +{ + MyGraph graph; + + // 1. Тест добавления вершин + for (int i = 0; i < 5; ++i) + { + std::string label = std::string(1, (char)('A' + i)); + size_t idx = graph.add_vertex(label); + if (idx != (size_t)i) + { + std::cout << "Invalid vertex index returned: " << idx << ", expected: " << i << "\n"; + return 1; + } + } + + if (graph.vertex_count() != 5) + { + std::cout << "Invalid vertex count: " << graph.vertex_count() << "\n"; + return 1; + } + + // Проверка меток вершин + for (int i = 0; i < 5; ++i) + { + std::string expected = std::string(1, (char)('A' + i)); + if (graph.get_vertex_label(i) != expected) + { + std::cout << "Invalid vertex label at " << i << "\n"; + return 1; + } + } + + // 2. Тест добавления ребер + graph.add_edge(0, 1, 10); + graph.add_edge(0, 2, 20); + graph.add_edge(1, 3, 30); + + if (!graph.has_edge(0, 1) || !graph.has_edge(0, 2) || !graph.has_edge(1, 3)) + { + std::cout << "Edges not found\n"; + return 1; + } + + if (graph.get_edge_label(0, 1) != 10 || graph.get_edge_label(0, 2) != 20) + { + std::cout << "Invalid edge labels\n"; + return 1; + } + + // 3. Тест итератора + { + auto it = graph.get_neighbor_iterator(0); + int count = 0; + bool found_1 = false; + bool found_2 = false; + + while (it.has_next()) + { + size_t neighbor_idx = it.next(); + count++; + + if (neighbor_idx == 1) found_1 = true; + else if (neighbor_idx == 2) found_2 = true; + else + { + std::cout << "Unexpected neighbor for A: " << neighbor_idx << "\n"; + return 1; + } + } + + if (count != 2 || !found_1 || !found_2) + { + std::cout << "Iterator failed to find all neighbors for A\n"; + return 1; + } + } + + // 4. Тест удаления ребра + graph.remove_edge(0, 2); + if (graph.has_edge(0, 2)) + { + std::cout << "Edge A->C should be removed\n"; + return 1; + } + + // 5. Тест удаления вершины + graph.remove_vertex(1); + + if (graph.vertex_count() != 4) + { + std::cout << "Invalid vertex count after removal\n"; + return 1; + } + + if (graph.get_vertex_label(1) != "C" || graph.get_vertex_label(2) != "D") + { + std::cout << "Vertex labels shift failed\n"; + return 1; + } + + if (graph.has_edge(0, 1)) + { + std::cout << "Edge 0->1 shouldn't exist\n"; + return 1; + } + + std::cout << "All tests passed!\n"; + return 0; +} diff --git a/LibraryCPPTemplate/Tests/vector.cpp b/LibraryCPPTemplate/Tests/vector.cpp index 7e549cacce..0752cb260f 100644 --- a/LibraryCPPTemplate/Tests/vector.cpp +++ b/LibraryCPPTemplate/Tests/vector.cpp @@ -15,7 +15,7 @@ int main() } for (size_t i = 0 ; i < vector.size() ; ++i) - vector.set(i, i); + vector.set(i, (int)i); vector = vector; diff --git a/LibraryCPPTemplate/graph.h b/LibraryCPPTemplate/graph.h new file mode 100644 index 0000000000..33643356d5 --- /dev/null +++ b/LibraryCPPTemplate/graph.h @@ -0,0 +1,188 @@ +#ifndef GRAPH_H +#define GRAPH_H +#include "vector.h" + +template +class Graph +{ +public: + struct Edge + { + bool exists; + ELabel label; + + Edge(): exists(false), label(ELabel()) {} + }; + + explicit Graph(size_t vertex_count = 0) + { + vertex_labels_.resize(vertex_count); + adj_matrix_.resize(vertex_count); + + for (size_t i = 0; i < vertex_count; ++i) + { + Vector row; + row.resize(vertex_count); + adj_matrix_.set(i, row); + } + } + + size_t vertex_count() const + { + return vertex_labels_.size(); + } + + size_t add_vertex(const VLabel& label) + { + size_t new_index = vertex_labels_.size(); + size_t new_size = new_index + 1; + + vertex_labels_.resize(new_size); + vertex_labels_[new_index] = label; + + for (size_t i = 0; i < new_index; ++i) + { + adj_matrix_[i].resize(new_size); + } + + Vector new_row; + new_row.resize(new_size); + adj_matrix_.resize(new_size); + adj_matrix_[new_index] = new_row; + + return new_index; + } + + void remove_vertex(size_t index) + { + size_t current_size = vertex_count(); + if (index >= current_size) return; + + for (size_t i = index; i < current_size - 1; ++i) + { + vertex_labels_[i] = vertex_labels_[i + 1]; + } + vertex_labels_.resize(current_size - 1); + + for (size_t i = index; i < current_size - 1; ++i) + { + adj_matrix_[i] = adj_matrix_[i + 1]; + } + adj_matrix_.resize(current_size - 1); + + for (size_t i = 0; i < adj_matrix_.size(); ++i) + { + Vector& row = adj_matrix_[i]; + for (size_t j = index; j < row.size() - 1; ++j) + { + row[j] = row[j + 1]; + } + row.resize(row.size() - 1); + } + } + + void add_edge(size_t from, size_t to, const ELabel& label) + { + if (from >= vertex_count() || to >= vertex_count()) return; + Edge& e = adj_matrix_[from][to]; + e.exists = true; + e.label = label; + } + + void remove_edge(size_t from, size_t to) + { + if (from >= vertex_count() || to >= vertex_count()) return; + Edge& e = adj_matrix_[from][to]; + e.exists = false; + } + + bool has_edge(size_t from, size_t to) const + { + if (from >= vertex_count() || to >= vertex_count()) return false; + return adj_matrix_.get(from).get(to).exists; + } + + ELabel get_edge_label(size_t from, size_t to) const + { + if (from >= vertex_count() || to >= vertex_count()) return ELabel(); + return adj_matrix_.get(from).get(to).label; + } + + void set_vertex_label(size_t index, const VLabel& label) + { + vertex_labels_.set(index, label); + } + + VLabel get_vertex_label(size_t index) const + { + return vertex_labels_.get(index); + } + + Vector get_all_vertex_labels() const + { + return vertex_labels_; + } + + class NeighborIterator + { + public: + NeighborIterator(const Graph* graph, size_t source_vertex) + : graph_(graph), source_(source_vertex), current_index_(0) + { + if (source_ >= graph_->vertex_count()) + { + current_index_ = graph_->vertex_count(); + } + else + { + find_next_valid(); + } + } + + bool has_next() const + { + return current_index_ < graph_->vertex_count(); + } + + size_t next() + { + size_t result = current_index_; + current_index_++; + find_next_valid(); + return result; + } + + ELabel current_edge_label() const + { + return graph_->adj_matrix_.get(source_).get(current_index_).label; + } + + private: + const Graph* graph_; + size_t source_; + size_t current_index_; + + void find_next_valid() + { + while (current_index_ < graph_->vertex_count()) + { + if (graph_->has_edge(source_, current_index_)) + { + return; + } + current_index_++; + } + } + }; + + NeighborIterator get_neighbor_iterator(size_t vertex_index) const + { + return NeighborIterator(this, vertex_index); + } + +private: + Vector vertex_labels_; + Vector> adj_matrix_; +}; + +#endif \ No newline at end of file diff --git a/LibraryCPPTemplate/vector.h b/LibraryCPPTemplate/vector.h index f4872259e1..fe0fc76d47 100644 --- a/LibraryCPPTemplate/vector.h +++ b/LibraryCPPTemplate/vector.h @@ -1,57 +1,128 @@ -#ifndef VECTOR_TEMPLATE_H -#define VECTOR_TEMPLATE_H +#ifndef VECTOR_H +#define VECTOR_H #include -template class Vector +template +class Vector { public: - // Creates vector - Vector() + Vector(): data_(nullptr), size_(0), capacity_(0) {} + + Vector(const Vector& a): data_(nullptr), size_(0), capacity_(0) + { + copy_from(a); + } + + Vector& operator=(const Vector& a) { + if (this != &a) + { + delete[] data_; + copy_from(a); + } + return *this; } - // copy constructor - Vector(const Vector &a) + T& operator[](size_t index) { + return data_[index]; } - // assignment operator - Vector &operator=(const Vector &a) + const T& operator[](size_t index) const { - return *this; + return data_[index]; } - // Deletes vector structure and internal data ~Vector() { + delete[] data_; } - // Retrieves vector element with the specified index - Data get(size_t index) const + T get(std::size_t index) const { - return Data(); + if (index >= size_) + { + return T(); + } + return data_[index]; } - // Sets vector element with the specified index - void set(size_t index, Data value) + void set(std::size_t index, T value) { + if (index >= size_) + { + return; + } + data_[index] = value; } - - // Retrieves current vector size - size_t size() const + std::size_t size() const { - return 0; + return size_; } - // Changes the vector size (may increase or decrease) - // Should be O(1) on average - void resize(size_t size) + void resize(std::size_t new_size) { + if (new_size <= capacity_) + { + if (new_size > size_) + { + for (std::size_t i = size_; i < new_size; ++i) + { + data_[i] = T(); + } + } + size_ = new_size; + return; + } + + std::size_t new_capacity = (capacity_ == 0 ? 1 : capacity_); + while (new_capacity < new_size) + { + new_capacity *= 2; + } + + T* new_data = new T[new_capacity]; + + for (std::size_t i = 0; i < size_; ++i) + { + new_data[i] = data_[i]; + } + for (std::size_t i = size_; i < new_size; ++i) + { + new_data[i] = T(); + } + + delete[] data_; + data_ = new_data; + capacity_ = new_capacity; + size_ = new_size; } private: - // private data should be here + T* data_; + std::size_t size_; + std::size_t capacity_; + + void copy_from(const Vector& a) + { + if (a.size_ > 0) + { + capacity_ = a.capacity_; + data_ = new T[capacity_]; + size_ = a.size_; + for (std::size_t i = 0; i < size_; ++i) + { + data_[i] = a.data_[i]; + } + } + else + { + data_ = nullptr; + size_ = 0; + capacity_ = 0; + } + } }; -#endif +#endif \ No newline at end of file diff --git a/README.md b/README.md index 8e5634ebf8..11ecbf970d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Дедюков Егор Алексеевич гр.: 5098ДУ ПТИ + # Algorithms [Как выполнять лабораторные работы](LABS.md)