├── .clang-format ├── .clang-tidy ├── .clippy-build-profiles.json ├── .clippy.json ├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── TODO.md ├── cmake ├── Ccache.cmake ├── CheckCompiler.cmake ├── CompileOptions.cmake ├── Development.cmake ├── Helpers.cmake ├── Logging.cmake ├── Platform.cmake ├── Sanitize.cmake ├── Task.cmake └── Werror.cmake ├── deadlines.yaml ├── docker ├── client │ ├── .gitignore │ ├── README.md │ ├── bin │ │ ├── create │ │ ├── login │ │ ├── remove │ │ ├── restart │ │ └── root │ └── pequod │ │ ├── __init__.py │ │ ├── client.py │ │ ├── config.py │ │ ├── helpers.py │ │ └── logging.py ├── config │ ├── bashrc │ └── keys │ │ └── .gitkeep ├── docker-compose.yml └── image │ ├── Dockerfile │ └── install_deps.sh ├── docs ├── Colloquium.md ├── ci.md ├── docker.md ├── faq.md ├── image.png ├── links.md ├── readme.md ├── setup.md └── style_guide.md ├── install_client.sh ├── library └── CMakeLists.txt └── tasks ├── CMakeLists.txt ├── README.md ├── abstract ├── CMakeLists.txt ├── deque │ ├── CMakeLists.txt │ ├── deque.hpp │ ├── image.png │ ├── readme.md │ ├── task.json │ └── tests │ │ └── dump ├── queue │ ├── CMakeLists.txt │ ├── queue.hpp │ ├── readme.md │ ├── task.json │ └── tests │ │ └── dump ├── readme.md └── stack │ ├── CMakeLists.txt │ ├── readme.md │ ├── stack.hpp │ ├── task.json │ └── tests │ └── dump ├── bash ├── CMakeLists.txt └── bash │ ├── CMakeLists.txt │ ├── readme.md │ ├── solution.py │ ├── solution.sh │ └── task.json ├── lists ├── CMakeLists.txt ├── forward │ ├── CMakeLists.txt │ ├── exceptions.hpp │ ├── forward_list.hpp │ ├── images │ │ └── image.png │ ├── readme.md │ ├── task.json │ └── tests │ │ ├── stress.cpp │ │ └── unit.cpp ├── list │ ├── CMakeLists.txt │ ├── exceptions.hpp │ ├── images │ │ ├── ds.png │ │ ├── image-1.png │ │ └── image.png │ ├── list.hpp │ ├── readme.md │ ├── task.json │ └── tests │ │ ├── stress.cpp │ │ └── unit.cpp └── readme.md ├── tree ├── CMakeLists.txt ├── NTree │ ├── CMakeLists.txt │ ├── filesystem │ │ ├── detail │ │ │ └── exceptions.hpp │ │ ├── files │ │ │ ├── directory.cpp │ │ │ ├── directory.hpp │ │ │ ├── file.cpp │ │ │ └── file.hpp │ │ ├── fs.cpp │ │ ├── fs.hpp │ │ └── map │ │ │ └── map.hpp │ ├── readme.md │ ├── task.json │ └── tests │ │ └── unit.cpp ├── bst │ ├── CMakeLists.txt │ ├── images │ │ └── image.png │ ├── map.hpp │ ├── readme.md │ ├── task.json │ └── tests │ │ ├── stress.cpp │ │ └── unit.cpp ├── iterators │ ├── CMakeLists.txt │ ├── map.hpp │ ├── readme.md │ ├── task.json │ └── tests │ │ ├── stress.cpp │ │ └── unit.cpp └── readme.md ├── tutorial ├── CMakeLists.txt └── aplusb │ ├── CMakeLists.txt │ ├── aplusb.hpp │ ├── readme.md │ ├── task.json │ └── tests │ ├── stress.cpp │ └── unit.cpp └── vector ├── CMakeLists.txt ├── readme.md └── vector ├── CMakeLists.txt ├── readme.md ├── task.json ├── tests ├── stress.cpp └── unit.cpp ├── vector.cpp └── vector.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 4 3 | AccessModifierOffset: -4 4 | ColumnLimit: 120 5 | AllowShortFunctionsOnASingleLine: None 6 | AllowShortIfStatementsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | DerivePointerAlignment: true 9 | KeepEmptyLinesAtTheStartOfBlocks: true 10 | SortIncludes: true 11 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '-*,bugprone-too-small-loop-variable, bugprone-use-after-move, cppcoreguidelines-avoid-goto, cppcoreguidelines-avoid-non-const-global-variables, cppcoreguidelines-init-variables, cppcoreguidelines-narrowing-conversions, cppcoreguidelines-pro-type-const-cast, google-build-using-namespace, google-explicit-constructor, google-readability-casting, google-runtime-int, modernize-replace-random-shuffle, modernize-use-bool-literals, modernize-use-override, modernize-use-using, performance-for-range-copy, readability-braces-around-statements, readability-container-size-empty, readability-duplicate-include, readability-identifier-naming, readability-isolate-declaration, readability-magic-numbers, readability-make-member-function-const, readability-redundant-control-flow, readability-redundant-string-init' 3 | HeaderFilterRegex: '\.h$' 4 | WarningsAsErrors: '*' 5 | CheckOptions: 6 | - key: readability-identifier-naming.NamespaceCase 7 | value: lower_case 8 | - key: readability-identifier-naming.ClassCase 9 | value: CamelCase 10 | - key: readability-identifier-naming.TypedefCase 11 | value: CamelCase 12 | - key: readability-identifier-naming.TypeAliasCase 13 | value: CamelCase 14 | - key: readability-identifier-naming.PrivateMemberSuffix 15 | value: '_' 16 | - key: readability-identifier-naming.StructCase 17 | value: CamelCase 18 | - key: readability-identifier-naming.FunctionCase 19 | value: CamelCase 20 | - key: readability-identifier-naming.VariableCase 21 | value: lower_case 22 | - key: readability-identifier-naming.PrivateMemberCase 23 | value: lower_case 24 | - key: readability-identifier-naming.ParameterCase 25 | value: lower_case 26 | - key: readability-identifier-naming.GlobalConstantCase 27 | value: UPPER_CASE 28 | - key: readability-identifier-naming.StaticConstantCase 29 | value: UPPER_CASE 30 | - key: readability-identifier-naming.ConstexprVariableCase 31 | value: CamelCase 32 | - key: google-runtime-int.TypeSuffix 33 | value: _t 34 | -------------------------------------------------------------------------------- /.clippy-build-profiles.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": [ 3 | "CMAKE_BUILD_TYPE=Debug", 4 | "UBSAN=ON" 5 | ], 6 | "DebugASan": [ 7 | "CMAKE_BUILD_TYPE=Debug", 8 | "ASAN=ON" 9 | ], 10 | "Release": [ 11 | "CMAKE_BUILD_TYPE=Release" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.clippy.json: -------------------------------------------------------------------------------- 1 | { 2 | "build_dir": "/tmp/clippy-build", 3 | "repo_master": "master", 4 | "solutions_master": "master", 5 | "cxx_compiler_binaries": [ 6 | "clang++-17", 7 | "clang++-15", 8 | "clang++" 9 | ], 10 | "c_compiler_binaries": [ 11 | "clang-17", 12 | "clang-15", 13 | "clang" 14 | ], 15 | "format_binaries": [ 16 | "clang-format-17", 17 | "clang-format-15", 18 | "clang-format" 19 | ], 20 | "tidy_binaries": [ 21 | "clang-tidy-17", 22 | "clang-tidy-15", 23 | "clang-tidy" 24 | ], 25 | "tidy_compiler_options": [ 26 | "-std=c++20", 27 | "-stdlib=libc++" 28 | ], 29 | "tidy_includes_path": "/tmp/clippy-build/Debug/_deps", 30 | "tidy_common_includes": [ 31 | "fmt-src/include" 32 | ], 33 | "forbidden": [ 34 | { 35 | "patterns": [ 36 | "Your code goes here", 37 | "Not implemented", 38 | "To be implemented" 39 | ], 40 | "hint": "Write code and remove this comment" 41 | }, 42 | { 43 | "patterns": [ 44 | "using namespace std" 45 | ], 46 | "hint": "Do not use this line! Never again!" 47 | }, 48 | { 49 | "patterns": [ 50 | "std::abort" 51 | ], 52 | "hint": "Write code and remove this stub" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @maxsmile123 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | Check: 8 | runs-on: ubuntu-22.04 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Install Clippy 13 | run: | 14 | git submodule init 15 | git submodule update --remote --merge 16 | chmod +x ./install_client.sh 17 | chmod +x ./client/install.py 18 | sudo bash ./docker/image/install_deps.sh 19 | echo "Y" | bash ./install_client.sh 20 | 21 | - name: Run Cmake 22 | run: | 23 | ./client/bin/clippy cmake 24 | 25 | - name: Run clang-format && clang-tidy 26 | run: | 27 | git fetch origin master:refs/remotes/origin/master 28 | BRANCH_VAR=$(git rev-parse origin/master) 29 | CURRENT_BRANCH=$(git branch --show-current) 30 | echo ${CURRENT_BRANCH} 31 | LIST_CHECK_FILES=$(git diff --name-only ${BRANCH_VAR} -- tasks/${CURRENT_BRANCH} | grep '\.cpp\|\.h\|\.hpp' | xargs dirname | sort -u) 32 | echo ${LIST_CHECK_FILES[@]} 33 | CUR_DIR=$(pwd) 34 | for file in ${LIST_CHECK_FILES[@]} 35 | do 36 | cd $file 37 | ls 38 | $CUR_DIR/client/bin/clippy validate 39 | cd $CUR_DIR 40 | done 41 | 42 | - name: Run tests 43 | run: | 44 | git fetch origin master:refs/remotes/origin/master 45 | BRANCH_VAR=$(git rev-parse origin/master) 46 | CURRENT_BRANCH=$(git branch --show-current) 47 | echo ${CURRENT_BRANCH} 48 | LIST_CHECK_FILES=$(git diff --name-only ${BRANCH_VAR} -- tasks/${CURRENT_BRANCH} | grep '\.cpp\|\.h\|\.hpp' | xargs dirname | sort -u) 49 | echo ${LIST_CHECK_FILES[@]} 50 | CUR_DIR=$(pwd) 51 | for file in ${LIST_CHECK_FILES[@]} 52 | do 53 | cd $file 54 | ls 55 | $CUR_DIR/client/bin/clippy test 56 | cd $CUR_DIR 57 | done -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *# 3 | a.out 4 | *.out 5 | *.a 6 | *.o 7 | .vscode/ 8 | build 9 | release* 10 | tsan* 11 | asan* 12 | __pycache__ 13 | .doit.db 14 | .idea 15 | *.swp 16 | *.swo 17 | perf.data* 18 | cmake-* 19 | cmake_* 20 | Makefile 21 | .submit-repo/ 22 | CMakeSettings.json 23 | CppProperties.json 24 | .vs/ 25 | build_asan 26 | .DS_Store 27 | .cache/ 28 | CMakeFiles 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "client"] 2 | path = client 3 | url = https://github.com/Maxsmile123/Clippy 4 | 5 | 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(AlgorithmsAndDataStructure-Course) 4 | 5 | include(cmake/Logging.cmake) 6 | include(cmake/Ccache.cmake) 7 | include(cmake/Helpers.cmake) 8 | include(cmake/CheckCompiler.cmake) 9 | include(cmake/CompileOptions.cmake) 10 | include(cmake/Sanitize.cmake) 11 | include(cmake/Platform.cmake) 12 | include(cmake/Development.cmake) 13 | 14 | add_subdirectory(library) 15 | # This must be included after library so that 16 | # third party code is not affected 17 | include(cmake/Werror.cmake) 18 | include(cmake/Task.cmake) 19 | 20 | add_subdirectory(tasks) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Algorithms & Data Structure Course 2 | 3 | _"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте"_ – Стив Макконнелл, "Совершенный код". 4 | 5 | --- 6 | 7 | ## Просьба 8 | 9 | При создании репозитория делайте его приватным! Пожалуйста, не выкладывайте решения задач в открытый доступ! 10 | 11 | Спасибо ❤️ 12 | 13 | ## Инструкции 14 | 15 | 1) 🏃 [Начало работы](docs/setup.md) 16 | 2) 🤖 [Как сдавать задачи (CI)](docs/ci.md) 17 | 3) 🧵 [Как правильно писать код](docs/style_guide.md) 18 | 4) 📎 [Материалы курса](docs/links.md) 19 | 20 | ## Навигация 21 | 22 | - [Задачи](/tasks) 23 | - [Документация](/docs) 24 | 25 | ## Библиотеки 26 | 27 | - [fmt](https://github.com/fmtlib/fmt) – форматированный вывод 28 | - [gtest](https://github.com/google/googletest) – фреймворк Google для тестирования 29 | - [benchmark](https://github.com/google/benchmark) - фреймворк Google для создания бенчмарков 30 | - [mimalloc](https://github.com/microsoft/mimalloc) – производительный аллокатор памяти от Microsoft -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | ## Critical 4 | - [x] Clippy коммитит задачу не в папку tasks 5 | 6 | ## BackLog 7 | - [x] CI падает, если нет cpp/h/hpp файлов 8 | - [ ] Репозиторий решений не подтягивает свежую структуру репозитория курса 9 | - [x] Clippy просит PAT при уже указанном токене в конфиге 10 | - [ ] Mimalloc собирается даже если сборка не Release 11 | - [ ] Ускорить установку clippy в CI 12 | -------------------------------------------------------------------------------- /cmake/Ccache.cmake: -------------------------------------------------------------------------------- 1 | find_program(CCACHE_FOUND ccache) 2 | if(CCACHE_FOUND) 3 | message(STATUS "Using ccache") 4 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) 5 | set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) 6 | endif(CCACHE_FOUND) 7 | 8 | -------------------------------------------------------------------------------- /cmake/CheckCompiler.cmake: -------------------------------------------------------------------------------- 1 | set(REQUIRED_CXX_COMPILER "Clang") 2 | set(CXX_COMPILER_MIN_VERSION 14.0) 3 | 4 | message(STATUS "C++ compiler: ${CMAKE_CXX_COMPILER}") 5 | 6 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL REQUIRED_CXX_COMPILER) 7 | if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS CXX_COMPILER_MIN_VERSION) 8 | message(FATAL_ERROR 9 | "Old version of ${REQUIRED_CXX_COMPILER} compiler: ${CMAKE_CXX_COMPILER_VERSION}, required ${CXX_COMPILER_MIN_VERSION}." 10 | ) 11 | endif() 12 | else() 13 | message(FATAL_ERROR 14 | "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}. Use ${REQUIRED_CXX_COMPILER}, version >= ${CXX_COMPILER_MIN_VERSION}." 15 | ) 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/CompileOptions.cmake: -------------------------------------------------------------------------------- 1 | # Common compile options for C++ 2 | 3 | set(CMAKE_CXX_STANDARD 20) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | set(CMAKE_CXX_EXTENSIONS OFF) 6 | 7 | # https://clang.llvm.org/docs/DiagnosticsReference.html 8 | add_compile_options(-Wall -Wextra -Wpedantic -g) 9 | 10 | # Turn warnings into errors 11 | add_compile_options(-Wno-language-extension-token) 12 | 13 | add_compile_options(-Wno-error=unused-command-line-argument) 14 | 15 | add_compile_options(-Wno-error=self-assign-overloaded) 16 | 17 | add_compile_options(-gdwarf-4) 18 | 19 | # libc++ 20 | add_compile_options(-stdlib=libc++) 21 | add_link_options(-stdlib=libc++) 22 | 23 | message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}") 24 | -------------------------------------------------------------------------------- /cmake/Development.cmake: -------------------------------------------------------------------------------- 1 | # https://cmake.org/cmake/help/v3.14/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html 2 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 3 | -------------------------------------------------------------------------------- /cmake/Helpers.cmake: -------------------------------------------------------------------------------- 1 | # Helper functions 2 | 3 | # Prepend ${PREFIX} to all arguments and put them in ${VAR} 4 | # Usage: prepend(FILES "${DIR}/" ${NAMES}) 5 | 6 | function(prepend VAR PREFIX) 7 | set(LIST_VAR "") 8 | foreach(ELEM ${ARGN}) 9 | list(APPEND LIST_VAR "${PREFIX}${ELEM}") 10 | endforeach() 11 | set(${VAR} "${LIST_VAR}" PARENT_SCOPE) 12 | endfunction() 13 | 14 | # Add ordered run target 15 | # Usage: run_chain("all_tests" ${TESTS}) 16 | 17 | function(run_chain NAME) 18 | set(BINARIES ${ARGN}) 19 | 20 | set(PREV_LINK_TARGET ${NAME}_init) 21 | 22 | add_custom_target(${NAME}_init true) 23 | 24 | foreach(BIN ${BINARIES}) 25 | set(LINK_TARGET "${NAME}_run_${BIN}") 26 | 27 | add_custom_target(${LINK_TARGET} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BIN}) 28 | add_dependencies(${LINK_TARGET} ${PREV_LINK_TARGET} ${BIN}) 29 | 30 | set(PREV_LINK_TARGET ${LINK_TARGET}) 31 | endforeach() 32 | 33 | add_custom_target(${NAME}) 34 | add_dependencies(${NAME} ${PREV_LINK_TARGET}) 35 | endfunction() 36 | -------------------------------------------------------------------------------- /cmake/Logging.cmake: -------------------------------------------------------------------------------- 1 | function(project_log message) 2 | message(STATUS "[${PROJECT_NAME}] ${message}") 3 | endfunction() 4 | -------------------------------------------------------------------------------- /cmake/Platform.cmake: -------------------------------------------------------------------------------- 1 | if(NOT DEFINED UNIX) 2 | set(UNIX false) 3 | endif() 4 | if(NOT DEFINED APPLE) 5 | set(APPLE false) 6 | endif() 7 | 8 | set(LINUX false) 9 | if(DEFINED UNIX AND CMAKE_SYSTEM_NAME STREQUAL "Linux") 10 | set(LINUX true) 11 | endif() 12 | 13 | add_definitions(-DUNIX=${UNIX}) 14 | add_definitions(-DAPPLE=${APPLE}) 15 | add_definitions(-DLINUX=${LINUX}) 16 | 17 | -------------------------------------------------------------------------------- /cmake/Sanitize.cmake: -------------------------------------------------------------------------------- 1 | # Sanitizers 2 | 3 | # -------------------------------------------------------------------- 4 | 5 | # UB Sanitizer 6 | # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html 7 | 8 | set(UBSAN_COMPILE_FLAGS -fsanitize=undefined -fno-sanitize-recover=all) 9 | set(UBSAN_LINK_FLAGS -fsanitize=undefined) 10 | 11 | if(UBSAN) 12 | message(STATUS "Sanitize with UB Sanitizer (UBSAN)") 13 | add_compile_options(${UBSAN_COMPILE_FLAGS}) 14 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${UBSAN_LINK_FLAGS}") 15 | endif() 16 | 17 | # -------------------------------------------------------------------- 18 | 19 | # Address Sanitizer 20 | # https://clang.llvm.org/docs/AddressSanitizer.html 21 | 22 | set(ASAN_COMPILE_FLAGS -fsanitize=address,undefined -fno-sanitize-recover=all) 23 | set(ASAN_LINK_FLAGS -fsanitize=address,undefined) 24 | 25 | if(ASAN) 26 | message(STATUS "Sanitize with Address Sanitizer (ASAN)") 27 | add_compile_options(${ASAN_COMPILE_FLAGS}) 28 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ASAN_LINK_FLAGS}") 29 | endif() 30 | 31 | -------------------------------------------------------------------------------- /cmake/Task.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin") 2 | 3 | # -------------------------------------------------------------------- 4 | 5 | set(LIBS_LIST "mimalloc;gtest;fmt;benchmark") 6 | 7 | 8 | # -------------------------------------------------------------------- 9 | 10 | # Helpers 11 | 12 | macro(get_task_target VAR NAME) 13 | set(${VAR} task_${TOPIC_NAME}_${TASK_NAME}_${NAME}) 14 | endmacro() 15 | 16 | function(add_task_executable BINARY_NAME) 17 | set(BINARY_SOURCES ${ARGN}) 18 | 19 | add_executable(${BINARY_NAME} ${BINARY_SOURCES} ${TASK_SOURCES}) 20 | target_link_libraries(${BINARY_NAME} pthread ${LIBS_LIST}) 21 | add_dependencies(${BINARY_NAME} ${LIBS_LIST}) 22 | endfunction() 23 | 24 | # -------------------------------------------------------------------- 25 | 26 | # Prologue 27 | 28 | macro(begin_task) 29 | set(TASK_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 30 | 31 | get_filename_component(TASK_NAME ${TASK_DIR} NAME) 32 | 33 | get_filename_component(TOPIC_DIR ${TASK_DIR} DIRECTORY) 34 | get_filename_component(TOPIC_NAME ${TOPIC_DIR} NAME) 35 | 36 | project_log("Topic = '${TOPIC_NAME}', task = '${TASK_NAME}'") 37 | 38 | include_directories(${TASK_DIR}) 39 | 40 | set(TEST_LIST "") 41 | endmacro() 42 | 43 | # -------------------------------------------------------------------- 44 | 45 | # Dependencies 46 | 47 | macro(task_link_libraries) 48 | list(APPEND LIBS_LIST ${ARGV}) 49 | endmacro() 50 | 51 | # -------------------------------------------------------------------- 52 | 53 | # Sources 54 | 55 | macro(set_task_sources) 56 | prepend(TASK_SOURCES "${TASK_DIR}/" ${ARGV}) 57 | endmacro() 58 | 59 | # -------------------------------------------------------------------- 60 | 61 | # Libraries 62 | 63 | function(add_task_library DIR_NAME) 64 | # Optional lib target name (dir name by default) 65 | if (${ARGC} GREATER 1) 66 | set(LIB_NAME ${ARGV1}) 67 | else() 68 | set(LIB_NAME ${DIR_NAME}) 69 | endif() 70 | 71 | set(LIB_DIR ${TASK_DIR}/${DIR_NAME}) 72 | 73 | get_task_target(LIB_TARGET ${LIB_NAME}) 74 | project_log("Add task library: ${LIB_TARGET}") 75 | 76 | file(GLOB_RECURSE LIB_CXX_SOURCES ${LIB_DIR}/*.cpp) 77 | file(GLOB_RECURSE LIB_HEADERS ${LIB_DIR}/*.hpp ${LIB_DIR}/*.ipp) 78 | 79 | get_filename_component(LIB_INCLUDE_DIR "${LIB_DIR}/.." ABSOLUTE) 80 | 81 | if (LIB_CXX_SOURCES) 82 | add_library(${LIB_TARGET} STATIC ${LIB_CXX_SOURCES} ${LIB_HEADERS}) 83 | target_include_directories(${LIB_TARGET} PUBLIC ${LIB_INCLUDE_DIR}) 84 | target_link_libraries(${LIB_TARGET} ${LIBS_LIST}) 85 | else() 86 | # header-only library 87 | add_library(${LIB_TARGET} INTERFACE) 88 | target_include_directories(${LIB_TARGET} INTERFACE ${LIB_INCLUDE_DIR}) 89 | target_link_libraries(${LIB_TARGET} INTERFACE ${LIBS_LIST}) 90 | endif() 91 | 92 | # Append ${LIB_TARGET to LIBS_LIST 93 | list(APPEND LIBS_LIST ${LIB_TARGET}) 94 | set(LIBS_LIST ${LIBS_LIST} PARENT_SCOPE) 95 | endfunction() 96 | 97 | # -------------------------------------------------------------------- 98 | 99 | # Custom target 100 | 101 | function(add_task_dir_target NAME DIR_NAME) 102 | get_task_target(TARGET_NAME ${NAME}) 103 | 104 | set(TARGET_DIR "${TASK_DIR}/${DIR_NAME}") 105 | file(GLOB_RECURSE TARGET_CXX_SOURCES ${TARGET_DIR}/*.cpp) 106 | 107 | add_task_executable(${TARGET_NAME} ${TARGET_CXX_SOURCES}) 108 | endfunction() 109 | 110 | # -------------------------------------------------------------------- 111 | 112 | # Tests 113 | 114 | function(add_task_test BINARY_NAME) 115 | get_task_target(TEST_NAME ${BINARY_NAME}) 116 | 117 | prepend(TEST_SOURCES "${TASK_DIR}/" ${ARGN}) 118 | add_task_executable(${TEST_NAME} ${TEST_SOURCES}) 119 | 120 | # Append test to TEST_LIST 121 | list(APPEND TEST_LIST ${TEST_NAME}) 122 | set(TEST_LIST "${TEST_LIST}" PARENT_SCOPE) 123 | endfunction() 124 | 125 | function(add_task_test_dir DIR_NAME) 126 | # Optional test target name (dir name by default) 127 | if (${ARGC} GREATER 1) 128 | set(BINARY_NAME ${ARGV1}) 129 | else() 130 | set(BINARY_NAME ${DIR_NAME}) 131 | endif() 132 | 133 | get_task_target(TEST_NAME ${BINARY_NAME}) 134 | 135 | set(TEST_DIR "${TASK_DIR}/${DIR_NAME}") 136 | file(GLOB_RECURSE TEST_CXX_SOURCES ${TEST_DIR}/*.cpp) 137 | 138 | add_task_executable(${TEST_NAME} ${TEST_CXX_SOURCES}) 139 | 140 | # Append test to TEST_LIST 141 | list(APPEND TEST_LIST ${TEST_NAME}) 142 | set(TEST_LIST "${TEST_LIST}" PARENT_SCOPE) 143 | endfunction() 144 | 145 | function(add_task_all_tests_target) 146 | get_task_target(ALL_TESTS_TARGET "run_all_tests") 147 | run_chain(${ALL_TESTS_TARGET} ${TEST_LIST}) 148 | endfunction() 149 | 150 | # -------------------------------------------------------------------- 151 | 152 | # Epilogue 153 | 154 | function(end_task) 155 | if(${TOOL_BUILD}) 156 | add_task_all_tests_target() 157 | endif() 158 | endfunction() 159 | -------------------------------------------------------------------------------- /cmake/Werror.cmake: -------------------------------------------------------------------------------- 1 | # Turn warnings into errors 2 | add_compile_options(-Werror) -------------------------------------------------------------------------------- /deadlines.yaml: -------------------------------------------------------------------------------- 1 | - group: Tutorial 2 | start: 17-02-2025 12:00 3 | deadline: 24-02-2025 23:59 4 | tasks: 5 | - task: tutorial/aplusb 6 | score: 100 7 | 8 | - group: Bash 9 | start: 24-02-2025 12:00 10 | deadline: 03-03-2025 23:59 11 | tasks: 12 | - task: bash/bash 13 | score: 150 14 | 15 | # Iterators and Templates. Classes 16 | - group: Lists 17 | start: 17-03-2025 12:00 18 | deadline: 07-04-2025 23:59 19 | tasks: 20 | - task: lists/list 21 | score: 200 22 | - task: lists/forward 23 | score: 250 24 | 25 | # Smart Pointers 26 | - group: Tree 27 | start: 31-03-2025 21:00 28 | deadline: 12-05-2025 23:59 29 | tasks: 30 | - task: tree/bst 31 | score: 300 32 | - task: tree/iterators 33 | score: 200 34 | - task: tree/NTree 35 | score: 200 36 | 37 | # Allocators and Move 38 | - group: Vector 39 | start: 07-04-2025 21:00 40 | deadline: 28-04-2025 23:59 41 | tasks: 42 | - task: vector/vector 43 | score: 300 44 | -------------------------------------------------------------------------------- /docker/client/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | 3 | env/ 4 | venv/ 5 | 6 | .idea/ 7 | -------------------------------------------------------------------------------- /docker/client/README.md: -------------------------------------------------------------------------------- 1 | # Pequod 🐋 2 | 3 | https://en.wikipedia.org/wiki/Pequod_(Moby-Dick) 4 | 5 | _Now the Lord had prepared a great fish to swallow up Jonah_ – Jonah 6 | 7 | --- 8 | 9 | Погружает рабочую копию репозитория, сабмодулем которого является, в докер-контейнер. 10 | 11 | ## Интеграция 12 | 13 | Ожидаемая структура репозитория: 14 | 15 | ``` 16 | {REPO_DIR} 17 | └── docker 18 | ├── client 19 | ├── config 20 | │ └── bashrc 21 | ├── image 22 | │ └── Dockerfile 23 | └── docker-compose.yml 24 | ``` 25 | 26 | В этой структуре `client` – данный репозиторий `pequod`, прицепленный сабмодулем. 27 | 28 | ### `docker-compose.yml` 29 | 30 | ```yaml 31 | version: "3.3" 32 | 33 | services: 34 | wrapper: 35 | build: 36 | context: image 37 | dockerfile: Dockerfile 38 | container_name: $CONTAINER_NAME 39 | cap_add: 40 | - SYS_PTRACE 41 | stdin_open: true 42 | tty: true 43 | volumes: 44 | - $HOST_WORKSPACE_DIR:/workspace 45 | ports: 46 | - "127.0.0.1:2227:22" 47 | ``` 48 | 49 | Путь `/workspace` для контейнера менять нельзя! 50 | 51 | ## Зависимости 52 | 53 | - `docker` 54 | - `docker-compose` 55 | - `python3` 56 | 57 | ## Команды 58 | 59 | Скрипты находятся в директории [bin](/bin) 60 | 61 | | Скрипт | Описание | 62 | | --- |--- | 63 | | `create` | Создать контейнер | 64 | | `login` | Залогиниться в контейнер под текущим пользователем | 65 | | `root` | Залогиниться в контейнер под рутом | 66 | | `restart` | Переподнять созданный контейнер | 67 | | `remove` | Удалить контейнер | 68 | 69 | ## Благодарности 70 | 71 | Идея и реализация – [Лев Хорошанский](https://github.com/TmLev) 72 | -------------------------------------------------------------------------------- /docker/client/bin/create: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).resolve().parent.parent)) 7 | 8 | from pequod import Client 9 | 10 | Client().create() 11 | -------------------------------------------------------------------------------- /docker/client/bin/login: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).resolve().parent.parent)) 7 | 8 | from pequod import Client 9 | 10 | Client().login() 11 | -------------------------------------------------------------------------------- /docker/client/bin/remove: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).resolve().parent.parent)) 7 | 8 | from pequod import Client 9 | 10 | Client().remove() 11 | -------------------------------------------------------------------------------- /docker/client/bin/restart: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).resolve().parent.parent)) 7 | 8 | from pequod import Client 9 | 10 | Client().restart() 11 | -------------------------------------------------------------------------------- /docker/client/bin/root: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).resolve().parent.parent)) 7 | 8 | from pequod import Client 9 | 10 | Client().root() 11 | -------------------------------------------------------------------------------- /docker/client/pequod/__init__.py: -------------------------------------------------------------------------------- 1 | from pequod.client import Client 2 | -------------------------------------------------------------------------------- /docker/client/pequod/client.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from typing import List 4 | 5 | from pequod import helpers 6 | from pequod.logging import log 7 | from pequod.config import ( 8 | REPO_NAME, 9 | CONTAINER_NAME, 10 | HOST_WORKSPACE_DIR, 11 | HOST_DOCKER_COMPOSE_PATH, 12 | CONTAINER_WORKSPACE_DIR, 13 | CONTAINER_BASHRC_PATH, 14 | ) 15 | 16 | 17 | class Client: 18 | """Orchestrator for a single Docker container. 19 | 20 | Usage: 21 | Client().login() # or one of the public methods below. 22 | """ 23 | 24 | @helpers.ensure_not_running_as_root 25 | def login(self) -> None: 26 | self._bash(user=f"{helpers.user_id()}:{helpers.group_id()}") 27 | 28 | @helpers.ensure_not_running_as_root 29 | def create(self) -> None: 30 | self._compose_config() 31 | self._compose_build() 32 | self._add_group() 33 | self._add_user() 34 | self._change_owner() 35 | log.info("Container was built successfully") 36 | 37 | def remove(self) -> None: 38 | self._stop() 39 | self._rm() 40 | 41 | @helpers.ensure_not_running_as_root 42 | def restart(self) -> None: 43 | self._compose_config() 44 | self._compose_restart() 45 | 46 | @helpers.ensure_not_running_as_root 47 | def root(self) -> None: 48 | self._bash(user="root") 49 | 50 | # -------------------------------------------------------------------------- 51 | # self.login() / self.root() 52 | 53 | def _bash(self, user: str) -> None: 54 | cmd = [ 55 | "--user", f"{user}", "--env", "REPO_NAME", f"{CONTAINER_NAME}", 56 | "/bin/bash", "--rcfile", f"{CONTAINER_BASHRC_PATH}" 57 | ] 58 | helpers_run_kwargs = {"panic_on_error": False, "log_on_error": False} 59 | self._exec(cmd, **helpers_run_kwargs) 60 | 61 | # -------------------------------------------------------------------------- 62 | # self.create() 63 | 64 | def _compose_build(self) -> None: 65 | cmd = [ 66 | "docker-compose", "-f", f"{HOST_DOCKER_COMPOSE_PATH}", "up", "-d", 67 | "--build", "--force-recreate" 68 | ] 69 | helpers.run(cmd, env=self._env) 70 | 71 | def _add_group(self) -> None: 72 | cmd = [ 73 | f"{CONTAINER_NAME}", "groupadd", "-g", f"{helpers.group_id()}", 74 | "grp" 75 | ] 76 | helpers_run_kwargs = {"panic_on_error": False} 77 | self._exec(cmd, **helpers_run_kwargs) 78 | 79 | def _add_user(self) -> None: 80 | cmd = [ 81 | f"{CONTAINER_NAME}", "useradd", "-u", f"{helpers.user_id()}", "-g", 82 | f"{helpers.group_id()}", "-m", f"{helpers.user()}" 83 | ] 84 | self._exec(cmd) 85 | 86 | def _change_owner(self) -> None: 87 | cmd = [ 88 | f"{CONTAINER_NAME}", "chown", "-R", f"{helpers.user()}", 89 | f"{CONTAINER_WORKSPACE_DIR}" 90 | ] 91 | self._exec(cmd) 92 | 93 | # -------------------------------------------------------------------------- 94 | # self.remove() 95 | 96 | def _stop(self) -> None: 97 | cmd = ["docker", "container", "stop", f"{CONTAINER_NAME}"] 98 | helpers.run(cmd, env=self._env) 99 | 100 | def _rm(self) -> None: 101 | cmd = ["docker", "container", "rm", f"{CONTAINER_NAME}"] 102 | helpers.run(cmd, env=self._env) 103 | 104 | # -------------------------------------------------------------------------- 105 | # self.restart() 106 | 107 | def _compose_restart(self) -> None: 108 | cmd = [ 109 | "docker-compose", "-f", f"{HOST_DOCKER_COMPOSE_PATH}", "up", "-d" 110 | ] 111 | helpers.run(cmd, env=self._env) 112 | 113 | # -------------------------------------------------------------------------- 114 | # Internal methods 115 | 116 | def _exec(self, cmd: List[str], interactive: bool = True, **kwargs) -> None: 117 | cmd = ["docker", "exec"] + (["-it"] if interactive else []) + cmd 118 | helpers.run(cmd, env=self._env, **kwargs) 119 | 120 | def _compose_config(self) -> None: 121 | cmd = ["docker-compose", "-f", f"{HOST_DOCKER_COMPOSE_PATH}", "config"] 122 | helpers.run(cmd, env=self._env) 123 | 124 | # -------------------------------------------------------------------------- 125 | # Initialisation 126 | 127 | def __init__(self) -> None: 128 | self._env = self._setup_env() 129 | 130 | def _setup_env(self) -> helpers.EnvLike: 131 | env: helpers.EnvLike = os.environ.copy() 132 | 133 | env.update({ 134 | "REPO_NAME": str(REPO_NAME), 135 | "CONTAINER_NAME": str(CONTAINER_NAME), 136 | "HOST_WORKSPACE_DIR": str(HOST_WORKSPACE_DIR), 137 | "CONTAINER_WORKSPACE_DIR": str(CONTAINER_WORKSPACE_DIR), 138 | "COMPOSE_PROJECT_NAME": str(REPO_NAME), 139 | }) 140 | 141 | return env 142 | -------------------------------------------------------------------------------- /docker/client/pequod/config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | # /path/to/{REPO_NAME}/docker/client/pequod/config.py 4 | THIS_MODULE_PATH = Path(__file__).resolve() 5 | 6 | HOST_REPO_DIR = THIS_MODULE_PATH.parent.parent.parent.parent 7 | REPO_NAME = HOST_REPO_DIR.name 8 | 9 | HOST_WORKSPACE_DIR = HOST_REPO_DIR.parent 10 | HOST_DOCKER_DIR = HOST_REPO_DIR / "docker" 11 | HOST_DOCKER_COMPOSE_PATH = HOST_DOCKER_DIR / "docker-compose.yml" 12 | 13 | CONTAINER_NAME = REPO_NAME 14 | 15 | CONTAINER_WORKSPACE_DIR = Path("/workspace") 16 | CONTAINER_REPO_DIR = CONTAINER_WORKSPACE_DIR / REPO_NAME 17 | CONTAINER_BASHRC_PATH = CONTAINER_REPO_DIR / "docker" / "config" / "bashrc" 18 | 19 | -------------------------------------------------------------------------------- /docker/client/pequod/helpers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess as sp 4 | 5 | from functools import wraps 6 | from typing import ( 7 | Any, 8 | List, 9 | Dict, 10 | Optional, 11 | ) 12 | from enum import ( 13 | Enum, 14 | auto, 15 | ) 16 | 17 | from pequod.logging import log 18 | 19 | 20 | EnvLike = Dict[str, str] 21 | 22 | 23 | class ErrorCode(Enum): 24 | PRECONDITION_FAILED = auto() 25 | CALLED_PROCESS_ERROR = auto() 26 | 27 | 28 | def run_as_root() -> bool: 29 | return os.geteuid() == 0 30 | 31 | 32 | def panic(reason: str, code: ErrorCode) -> None: 33 | log.critical(reason) 34 | exit(code) 35 | 36 | 37 | def ensure_not_running_as_root(fn) -> Any: 38 | 39 | @wraps(fn) 40 | def wrapper(*args, **kwargs): 41 | if run_as_root(): 42 | panic( 43 | reason= 44 | "This script must be run as non-root user inside docker group", 45 | code=ErrorCode.PRECONDITION_FAILED) 46 | 47 | return fn(*args, **kwargs) 48 | 49 | return wrapper 50 | 51 | 52 | def user_id() -> int: 53 | return os.geteuid() 54 | 55 | 56 | def group_id() -> int: 57 | return os.getegid() 58 | 59 | 60 | def user() -> str: 61 | return os.environ["USER"] 62 | 63 | 64 | def run( 65 | cmd: List[str], 66 | env: Optional[EnvLike] = None, 67 | panic_on_error: bool = True, 68 | log_on_error: bool = True, 69 | ) -> None: 70 | shell_cmd = " ".join(cmd) 71 | log.debug(f"Running `{shell_cmd}`") 72 | 73 | completed = sp.run(cmd, env=env, stdout=sys.stdout, stderr=sys.stderr) 74 | 75 | if completed.returncode != 0: 76 | msg = f"Called process `{shell_cmd}` exited with code {completed.returncode}" 77 | if panic_on_error: 78 | panic(reason=msg, code=ErrorCode.CALLED_PROCESS_ERROR) 79 | 80 | if log_on_error: 81 | log.warning(msg) 82 | -------------------------------------------------------------------------------- /docker/client/pequod/logging.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | LOG_LEVEL = os.environ.get("PEQUOD_LOG_LEVEL", "INFO").upper() 5 | logging.basicConfig(level=LOG_LEVEL, 6 | format="[%(name)s] [%(levelname)s] %(message)s") 7 | log = logging.getLogger("Pequod") 8 | -------------------------------------------------------------------------------- /docker/config/bashrc: -------------------------------------------------------------------------------- 1 | cat < /dev/null 22 | ssh-add $KEY >/dev/null 2>&1 23 | 24 | # ------------------------------------------------------------------------------ 25 | 26 | # Starting directory 27 | 28 | cd "/workspace/${REPO_NAME}" 29 | -------------------------------------------------------------------------------- /docker/config/keys/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/docker/config/keys/.gitkeep -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | wrapper: 5 | platform: linux/x86_64 6 | build: 7 | context: image 8 | dockerfile: Dockerfile 9 | container_name: $CONTAINER_NAME 10 | cap_add: 11 | - SYS_PTRACE 12 | stdin_open: true 13 | tty: true 14 | volumes: 15 | - $HOST_WORKSPACE_DIR:/workspace 16 | ports: 17 | - "127.0.0.1:3333:22" 18 | -------------------------------------------------------------------------------- /docker/image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | COPY install_deps.sh / 4 | RUN sh install_deps.sh 5 | 6 | ENV TZ=Europe/Moscow 7 | 8 | ENV LANG C.UTF-8 9 | ENV LC_ALL C.UTF-8 10 | 11 | ENV CXX="/usr/bin/clang++-17" 12 | ENV CC="/usr/bin/clang-17" 13 | 14 | # SSH 15 | EXPOSE 22 16 | RUN echo "StrictHostKeyChecking=no" >> /etc/ssh/ssh_config 17 | RUN mkdir /var/run/sshd 18 | CMD ["/usr/sbin/sshd", "-D"] 19 | -------------------------------------------------------------------------------- /docker/image/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -x 4 | 5 | echo "Installing dependencies" 6 | 7 | apt-get update 8 | 9 | DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata 10 | 11 | apt-get install -y software-properties-common 12 | 13 | add-apt-repository -y ppa:ubuntu-toolchain-r/test 14 | 15 | apt-get install -y wget rsync 16 | 17 | # LLVM 18 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 19 | add-apt-repository -y "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" 20 | 21 | # CMake 22 | wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc | apt-key add - 23 | apt-add-repository 'deb https://apt.kitware.com/ubuntu/ jammy main' 24 | 25 | apt-get update 26 | 27 | apt-get install -y \ 28 | sudo \ 29 | ssh \ 30 | vim \ 31 | make \ 32 | cmake \ 33 | build-essential \ 34 | ninja-build \ 35 | git \ 36 | linux-tools-common \ 37 | linux-tools-generic \ 38 | g++-13 \ 39 | clang-17 \ 40 | clang-format-17 \ 41 | clang-tidy-17 \ 42 | libc++-17-dev \ 43 | libc++abi-17-dev \ 44 | libclang-rt-17-dev \ 45 | clangd-17 \ 46 | lldb-17 \ 47 | gdb \ 48 | binutils-dev \ 49 | libdwarf-dev \ 50 | libdw-dev \ 51 | python3 \ 52 | python3-pip \ 53 | python3-venv \ 54 | python3-setuptools \ 55 | ca-certificates \ 56 | openssh-server \ 57 | autoconf 58 | 59 | pip3 install \ 60 | click \ 61 | gitpython \ 62 | termcolor \ 63 | virtualenv 64 | -------------------------------------------------------------------------------- /docs/Colloquium.md: -------------------------------------------------------------------------------- 1 | # Вопросы к Коллоквиуму по C++ по курсу "Алгоритмы и структуры данных" 2 | \* - Вопросы только для тех, кто идёт на 5 в семестре. Претентенды на оценку будут известны в мае 3 | 4 | 5 | ## Указатели 6 | 7 | - Какие операции есть у указателей? 8 | - Что будет при сложении указателя с int? 9 | - Как работает вычитание указателей? 10 | - Что такое `void*`? 11 | - Что такое `nullptr`? Отличие от `NULL`. 12 | - Что такое динамическая память? 13 | - Как работают операторы `new`, `new[]`, `delete`, `delete[]` 14 | 15 | ## Функции 16 | 17 | - Зачем нужна перегрузка функций? 18 | - Что такое `overloading resolution` и когда она применяется? 19 | - Что такое `promotion` и `conversion` в overloading resolution? 20 | - В каком порядке должны идти аргументы по умолчанию в функциях? 21 | 22 | ## Ссылки 23 | 24 | - Что такое ссылка? 25 | - Чем первое отличается от второго: 26 | ```c++ 27 | int a; 28 | int b = a; 29 | ``` 30 | 31 | ```c++ 32 | int a; 33 | int& b = a; 34 | ``` 35 | 36 | - Что распечатается? 37 | ```C++ 38 | int x = 1; 39 | int& y = x; 40 | int& z = y; 41 | int a = 0; 42 | z = a; 43 | std::cout << z << x << std::endl; 44 | ``` 45 | 46 | - Что тут не так? 47 | ```c++ 48 | std::string& hello(){ 49 | std::string res = "Hello"; 50 | return res; 51 | } 52 | ``` 53 | 54 | ## Const & static 55 | 56 | - Что такое `static` переменная? 57 | - Что такое `const` переменная? 58 | - Что нельзя сделать с `const` переменной? 59 | - Какая особенность у `const T*`? 60 | - Зачем принимать по константной ссылке? 61 | 62 | ## Приведение типов 63 | 64 | - Что такое `static_cast`? 65 | - Что такое `reinterpret_cast` и почему он не рекомендуется к использованию? 66 | - Что такое `const_cast` и почему он не рекомендуется к использованию? 67 | - Что такое `C-style cast` и почему он не рекомендуется к использованию? 68 | 69 | ## Классы и объекты 70 | 71 | - Чем классы отличаются от структур в C++? 72 | - Что такое `this`? 73 | - Что делает оператор `->`? 74 | - Что делает конструктор копирования? 75 | - Что такое `Shallow copy` и `Deep copy`? 76 | - Что напечатается? 77 | ```C++ 78 | class A{ 79 | A(){ 80 | std::cout << "A\n"; 81 | } 82 | ~A(){ 83 | std::cout << "~A\n"; 84 | } 85 | } 86 | class B{ 87 | A a; 88 | B(){ 89 | std::cout << "B\n"; 90 | } 91 | ~B(){ 92 | std::cout << "~B\n"; 93 | } 94 | } 95 | 96 | main () { 97 | B b; 98 | } 99 | 100 | ``` 101 | 102 | - Что нужно писать в деструкторах? 103 | - Чем конструктор копирования отличается от оператора `=`? 104 | - Как заставить компилятор сгенерировать констурктор по умолчанию? 105 | - Как запретить компилятор генерировать констурктор по умолчанию? 106 | - Что такое делегирующие конструкторы?* 107 | 108 | ## Перегрузка операторов 109 | 110 | - Что должен делать оператор копирования? 111 | - Что будет? 112 | ```C++ 113 | class String{ 114 | public: 115 | char* buffer; 116 | size_t sz; 117 | 118 | String() { 119 | buffer = new char[128]; 120 | } 121 | 122 | ~String() { 123 | delete[] buffer; 124 | } 125 | 126 | String& operator=(const String& other){ 127 | delete buffer; 128 | this->buffere = other.buffer; 129 | this->sz = other.sz; 130 | } 131 | } 132 | 133 | int main{ 134 | String s; 135 | s = s; 136 | } 137 | ``` 138 | 139 | - Как явно вызвать деструктор? Какие подводные камни у этого есть? 140 | - Что такое `Copy and Swap`? 141 | - Что такое правило трёх(и пяти)? 142 | - Зачем нужны списки инициализации в конструкторах? 143 | ```C++ 144 | class String{ 145 | public: 146 | char* buffer; 147 | size_t sz; 148 | 149 | String(): sz(128), buffer(new char[128]) {} 150 | 151 | ``` 152 | 153 | - Что проиницилизируется первым в примере выше - buffer или sz? 154 | - Что такое `Return Value Optimization(RVO)`? 155 | - Почему `++Object` лучше `Object++`? 156 | - Как перегружать операторы каста? Чтобы делать так: 157 | ``` 158 | 159 | int main(){ 160 | String s = "124"; 161 | int a = (int)s; 162 | } 163 | 164 | ``` 165 | 166 | ## Ключевые слова в методах 167 | 168 | - Зачем нужны `const` методы? Какие у них свойства? 169 | - Что такое ключевое слово `mutable`? Когда можно использовать? 170 | - Что такое `static` поле? 171 | - Что такое `static` метод? 172 | - Что будет при `delete nullptr`? 173 | - Ключевое слово `explicit` - зачем и когда использовать? 174 | 175 | 176 | ## Наследование* 177 | 178 | - Что такое `public`, `private`, `protected` наследования? 179 | - Как узнать получим ли мы доступ к Base переменной класса и вложенном наследовании? 180 | - Как явно вызвать метод родителя у наследника? 181 | - Можно ли унаследовать конструкторы? 182 | - В каком порядке вызываются конструкторы и деструкторы при наследовании? 183 | - Как инициализировать родителя при вызове конструктора наследника? 184 | - Что такое `Empty Base Optimization`? 185 | - Можно ли родителя кастовать к наследнику? 186 | - Можно ли кастовать при private наследовании? 187 | 188 | ## Шаблоны (Templates) 189 | 190 | - Что такое template и зачем он нужен? 191 | - Чем `Typename T` отличается от `Class T`? 192 | - Как работает перегрузка шаблонных функций? 193 | - Как явно вызвать шаблонную функцию? 194 | - Что такое специализация шаблона? 195 | - Что такое частичная специализация? 196 | - Шаблоны можно создавать только от типов? 197 | - Можно ли в такие типы подставлять не const значения? 198 | - Как можно сравнить два типа на этапе компиляции? 199 | - Как убрать от объекта всё лишнее - `*`, `&`, `const` и т.д? 200 | - Как работать с шаблонами от множества аргументов? 201 | 202 | 203 | ## Исключения 204 | 205 | - Что происходит во время выполнения кода: 206 | ```C++ 207 | throw 1; 208 | ``` 209 | - Чем отличается exception от RE? 210 | - Как поймать любое исключение? 211 | - Какие два оператора в C++ имеют исключения? 212 | - Создают ли копии объектов throw и catch? 213 | - Производится ли касты типов в catch? 214 | - Что такое RAII? 215 | - Что такое noexcept? 216 | - Что такое weak/strong exception safety? 217 | 218 | ## Итераторы в C++ 219 | - Что такое инвалидация итераторов? 220 | - Какие есть типы итераторов? 221 | - Что делает `std::advance`? 222 | - Что делает `std::next`? 223 | - Для чего используется `iterator_traits`? 224 | - Что делает `std::distance`? 225 | 226 | ## Аллокаторы 227 | 228 | - Зачем перегружать операторы new и delete? 229 | - Как перегрузить глобальный new? 230 | - Опишите, как работает new и delete? 231 | - Как перегружать new/delete для своего класса? 232 | - Как работает `placement new`? 233 | - Что такое аллокатор? 234 | - Зачем нужно `allocator_traits`? 235 | - Какие бывают типы аллокаторов? 236 | 237 | ## Move-семантика 238 | - Что делает `std::move`? 239 | - Правило пяти 240 | - Что такое `std::forward`? 241 | - Что такое rvalue, lvalue, универсальные ссылки? 242 | 243 | ## Умные указатели 244 | - Зачем нужны? 245 | - Что делает `std::uniqie_ptr`? 246 | - Что делает `std::shared_ptr`? 247 | - Что делает `std::weak_ptr`? 248 | -------------------------------------------------------------------------------- /docs/ci.md: -------------------------------------------------------------------------------- 1 | # Автоматическое тестирование (CI) 2 | 3 | ## Просьба 4 | 5 | Пожалуйста 🙏, не отправляйте в CI решения, которые не проходят тесты (`clippy test`) локально: в CI запускаются те же самые тесты с помощью той же самой команды. 6 | 7 | ## Шаги, которые нужно выполнить 1 раз 8 | 9 | ### Регистрация 10 | 11 | Заведите аккаунт на https://github.com/, если у вас его нет. 12 | 13 | Не используйте в логине дефис. 14 | 15 | ### Создайте себе репозиторий с решениями используя исходный как шаблон 16 | 17 | ![Alt text](image.png) 18 | 19 | Сделайте его `приватным!` Не называйте его так же, как репозиторий курса! 20 | 21 | ### Аттач репозитория с решениями 22 | 23 | В этот репозиторий вы будете коммитить и пушить решения (только не вручную, а через консольный клиент `clippy`), там же будут запускаться автоматические тесты. 24 | 25 | #### Шаг 1 26 | 27 | Перейдите в локальную копию репозитория курса: 28 | ```shell 29 | cd /workspace/algo-2025 30 | ``` 31 | 32 | #### Шаг 2 33 | 34 | Прикрепите к репозиторию курса репозиторий с решениями: 35 | ```shell 36 | clippy attach {solutions-repo-url} 37 | ``` 38 | Вместо `{solutions-repo-url}` подставьте url созданного только что репозитория. 39 | 40 | Если имя созданного мною репозитория - `algo-2025-solution`, то команда будет выглядеть так: 41 | ```shell 42 | clippy attach https://github.com/Maxsmile123/algo-2025-solution 43 | ``` 44 | 45 | Команду `attach` можно выполнить повторно: существующая локальная копия сотрется (с вашего явного разрешения), но это не страшно, если вы запушили все ваши коммиты с помощью `clippy push` в remote репозиторий. 46 | 47 | ### Настраиваем репозиторий решений 48 | 49 | #### Шаг 1. Выдать мне доступ 50 | 51 | Перейдите во вкладку `Settings` своего репозитория. Далее переходим в `Collaborators`. Нажимаем `Add people` и пишем там мой ник: ```Maxsmile123```. 52 | 53 | Этим действием вы выдадите мне доступ на просмотр вашего репозитория. 54 | 55 | ### Настраиваем локально 56 | 57 | #### Шаг 1. Смотрим конфиг 58 | 59 | Выполните команду 60 | ```shell 61 | clippy show-config 62 | ``` 63 | 64 | Вы увидите поля конфига, который нужно заполнить для посылки решений задач. 65 | 66 | Эти поля мы заполним на последующих шагах. 67 | 68 | #### Шаг 2. Выдайте себе PAT 69 | 70 | На этом шаге вам нужно получить токен для работы с API https://github.com/ 71 | 72 | Для этого на https://github.com/ зайдите в `Settings` своего профиля, затем в `Developer Settings`, после в `Personal access tokens`, там нажмите `Generate new token (Classic)` после чего выберите произвольное имя для вашего токена, поставьте галки напротив (repo, admin:org), установите expiration date и нажмите `Generate token`. 73 | 74 | См. [Personal Access Tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) 75 | 76 | Токен – это полученная случайная строчка из цифр и букв. Сохраните его, иначе он пропадет! Имя токена нам не понадобится. 77 | 78 | #### Шаг 3. Запишем PAT в конфиг 79 | 80 | Установите полученный токен в `clippy`: 81 | ```shell 82 | # Магический разделитель -- нужен на случай дефисов в начале токена 83 | clippy config -- github.token {your-github-token} 84 | ``` 85 | 86 | Здесь `{your-github-token}` – токен, полученный вами на предыдущем шаге. 87 | 88 | Токен нужен `clippy` для создания pull request-ов (PR). 89 | 90 | #### Шаг 4. Заполняем конфиг 91 | 92 | Задайте _assignee_ для ваших PR: 93 | ```shell 94 | # Подставьте в команду логин своего семинариста на github.com 95 | clippy config assignee Maxsmile123 96 | ``` 97 | 98 | Настройте теги для ваших PR: 99 | ```shell 100 | # Теги перечисляются через запятую, без лишних пробелов 101 | # Вместо 123 подставьте номер вашей академической группы (если она у вас есть) 102 | clippy config tags 'Labs,123' 103 | ``` 104 | 105 | Настройте группу для ваших PR: 106 | 107 | ```shell 108 | # Вместо 123 подставьте номер вашей академической группы (если она у вас есть) 109 | clippy config group 123 110 | ``` 111 | 112 | Настройте фамилию и имя для ваших PR: 113 | 114 | ```shell 115 | # Вместо Maxim подставьте ваше имя 116 | # Вместо Sysoev подставьте вашу фамилию 117 | clippy config name.first Maxim 118 | clippy config name.last Sysoev 119 | ``` 120 | 121 | Настройте свой ник на GitHub для ваших PR: 122 | 123 | ```shell 124 | # Вместо Maxsmile123TEST подставьте свой ник 125 | clippy config github.user Maxsmile123TEST 126 | ``` 127 | 128 | ## Работа с задачей / репозиторием решений 129 | 130 | #### Шаг 1 131 | 132 | Переходим в директорию с задачей: 133 | ```shell 134 | cd tasks/tutorial/aplusb/ 135 | ``` 136 | 137 | #### Шаг 2 138 | 139 | Решаем задачу. 140 | 141 | #### Шаг 3 142 | 143 | Коммитим файлы текущей задачи в отдельную ветку локального репозитория решений: 144 | ```shell 145 | clippy commit 146 | ``` 147 | 148 | Можно указать комментарий: 149 | ```shell 150 | clippy commit -m "yep" 151 | ``` 152 | 153 | #### Шаг 4 154 | 155 | Пушим коммиты из локальной ветки задачи в ветку вашего приватного remote-репозитория: 156 | ```shell 157 | clippy push 158 | ``` 159 | 160 | #### Шаг 5 161 | 162 | Создаем [_pull request_](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) ветки с решением в _master_: 163 | 164 | ```shell 165 | # Или короче: clippy pr 166 | clippy pull-request 167 | ``` 168 | на созданном PR будет запускаться CI. 169 | 170 | #### Дальнейшие шаги 171 | 172 | Пусть дальше вы нашли и исправили баги в своем коде: 173 | 174 | ```shell 175 | # ... 176 | clippy commit -m 'fix' 177 | # ... 178 | clippy commit -m 'another fix' 179 | clippy push 180 | ``` 181 | 182 | После `push` новые коммиты попадут в уже существующий PR и перезапустят CI. 183 | 184 | ## Под капотом 185 | 186 | Для каждой задачи в этом репозитории будет создаваться отдельная ветка (например, `tutorial/aplusb`). 187 | 188 | Команда `clippy commit` коммитит файлы текущей задачи (см. `submit_files` в `task.json`) _локально_ в ваш репозиторий решений. 189 | 190 | Команда `clippy push` пушит коммиты из ветки текущей задачи локального репозитория решений в remote-репозиторий. 191 | 192 | 193 | [Документация по командам](https://github.com/Maxsmile123/Clippy/blob/master/docs%2Fcommands.md) 194 | -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | # Контейнер для курса 2 | 3 | ## Зависимости 4 | 5 | ### Linux 6 | 7 | ```shell 8 | sudo apt update 9 | sudo apt install -y docker docker-compose bash 10 | sudo groupadd docker 11 | sudo usermod -aG docker $USER 12 | ``` 13 | 14 | После этого нужно перезапустить текущую сессию (или просто перезагрузить машину). 15 | 16 | Убедитесь, что Докер успешно установился и работает: 17 | ```shell 18 | docker run hello-world 19 | ``` 20 | 21 | ### OS X / Windows 22 | 23 | Скачайте, установите и запустите Docker Desktop: 24 | [Mac OS](https://docs.docker.com/desktop/install/mac-install/) 25 | [Windows](https://docs.docker.com/desktop/install/windows-install/) 26 | 27 | Убедитесь, что в тулбаре появился значок с 🐳 китом. 28 | 29 | Запустите Docker Desktop. 30 | **Для Windows**: В настройках установите использовать WSL2 как бэкенд. 31 | 32 | ## ARM 33 | 34 | Если вы будете проходить курс на машине с процессором ARM, то перед созданием контейнера нужно добавить в [`docker-compose.yml`](/docker/docker-compose.yml) в секцию `wrapper` настройку `platform: linux/x86_64`. 35 | 36 | ## Создание контейнера 37 | 38 | Запустите в WSL/Mac скрипт `create` из `docker/client/bin`: 39 | ```shell 40 | ./docker/client/bin/create 41 | ``` 42 | 43 | Созданный контейнер будет называться так же, как и репозиторий курса. 44 | 45 | Директория с репозиторием курса монтируется в контейнер по пути `/workspace`. 46 | 47 | ## Логин в контейнер 48 | 49 | Запустите в хост-системе скрипт `login` из `docker/client/bin`. 50 | ```shell 51 | ./docker/client/bin/login 52 | ``` 53 | 54 | Вы успешно вошли в контейнер, если видите приветствие: 55 | ``` 56 | Hi, ${USERNAME}! 57 | Welcome to Docker container 'algo-2025'! 58 | (___-){ 59 | =================================== 60 | 61 | Type 'exit' to exit =) 62 | ``` 63 | 64 | Логиниться в контейнер необходимо для работы с клиентом, 65 | редактировать файлы можно и снаружи (для этого рекомендуется использовать IDE). 66 | 67 | Работайте с контейнером под тем пользователем, под которым проводили установку! 68 | 69 | 70 | ## Выход из контейнера 71 | 72 | Для выхода из контейнера в терминале выполните команду `exit`. 73 | 74 | ## Перезапуск контейнера 75 | 76 | Запустите в хост-системе скрипт `restart` из `docker/client/bin`. 77 | 78 | Может быть полезно, если вы выключите компьютер / исчезнет контейнер / произойдет что-то непонятное. 79 | 80 | ## Рабочая директория 81 | 82 | При создании контейнера рабочая директория, в которую был склонирован репозиторий курса, монтируется в контейнере в директорию `/workspace`. 83 | 84 | Все изменения, которые вы сделаете в директории `/workspace` в контейнере, применяются в хост-системе. И наоборот: если вы редактируете файлы в IDE в хост-системе, то изменения будут видны и внутри контейнера. 85 | 86 | Удалить и пересоздать контейнер – всегда безопасно, вы не потеряете написанный код. 87 | 88 | ## `.bashrc` 89 | 90 | По умолчанию при логине в контейнер вы оказываетесь в корне репозитория курса. 91 | 92 | Если вы хотите изменить точку входа (например, переходить при логине сразу в директорию `tasks`), то поменяйте в хост-системе файл [`docker/config/bashrc`](/docker/config/bashrc). 93 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | #### Рабочее окружение сломалось, как быть? 4 | 5 | В любой непонятной ситуации проще пересоздать рабочее окружение по инструкции, а не разбираться как починить разломанное. 6 | 7 | --- 8 | 9 | #### Не работает докер 10 | 11 | В любой непонятной ситуации с докером лучше пересоздать образ. Для этого удалите все текущие образы и сбросьте кэши: 12 | ```shell 13 | docker rm -vf $(docker ps -a -q) 14 | docker rmi -f $(docker images -a -q) 15 | ``` 16 | 17 | А после создайте заново образ по [инструкции](docker.md) 18 | 19 | --- 20 | 21 | 22 | #### Не найден clang++ в докер контейнере: 23 | 24 | 1. Выйдите из контейнера - exit 25 | 2. Пропишите в терминале Windows/OS X/Linux: 26 | ```shell 27 | docker exec -u 0 -it containerName bash 28 | ``` 29 | Где вместо containerName подставьте имя только что созданного контейнера (имя репозитория).
30 | 3. Пропишите 31 | ```shell 32 | sudo ln -s /usr/bin/clang-17 /usr/bin/clang 33 | sudo ln -s /usr/bin/clang++-17 /usr/bin/clang++ 34 | sudo ln -s /usr/bin/clang-tidy-17 /usr/bin/clang-tidy 35 | sudo ln -s /usr/bin/clangd-17 /usr/bin/clangd 36 | sudo ln -s /usr/bin/clang-format-17 /usr/bin/clang-format 37 | 38 | ``` 39 | 40 | #### Как отключить запуск тестов в подпроцессе (например, для запуска тестов с отладчиком)? 41 | 42 | Запустите исполняемый файл с тестами с флагами командной строки `--disable_forks` и `--disable_time_limits`. 43 | 44 | --- 45 | 46 | #### При компиляции ворнинги считаются ошибками, как это отключить? 47 | 48 | В промышленной разработке `-Werror` – стандарт. Но при отладке решения задачи этот флаг может мешать. 49 | 50 | Выключить его можно закомментировав соответствующий `add_compile_options` в [`cmake/CompileOptions.cmake`](/cmake/CompileOptions.cmake). 51 | 52 | --- 53 | 54 | #### Не работает github.com? 55 | 56 | Такое случается. Проверить его статус можно здесь: https://www.githubstatus.com/. 57 | 58 | --- 59 | 60 | #### Проект курса не собирается! 61 | 62 | Обратите внимание: сборка через Clippy и сборка через CLion работают независимо друг от друга! 63 | 64 | ##### Clippy 65 | 66 | Попробуйте собраться начисто: 67 | 68 | `clippy cmake --clean`, затем `clippy warmup`. 69 | 70 | Вместо `--clean` можно стереть директорию `build` в корне репозитория курса и выполнить обычный `clippy cmake`. 71 | 72 | 73 | #### Как установить опцию CMake при сборке? 74 | 75 | Для Clippy используйте файл `.clippy-build-profiles.json` в корне репозитория. 76 | 77 | --- 78 | 79 | #### Утилита `clang-tidy` / команда `clippy tidy` генерирует тысячи ворнингов, так и должно быть? 80 | 81 | К сожалению, да. 82 | 83 | --- 84 | 85 | #### Как отключить проверку `clang-tidy` для конкретной строки кода? 86 | 87 | См. [Suppressing Undesired Diagnostics](https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics). 88 | 89 | --- 90 | 91 | #### У меня MacBook с процессором ARM M1/M2, код не собирается, что делать? 92 | 93 | Не поддерживаем, извините. 94 | 95 | -------------------------------------------------------------------------------- /docs/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/docs/image.png -------------------------------------------------------------------------------- /docs/links.md: -------------------------------------------------------------------------------- 1 | # Полезные статьи для изучения курса 2 | 3 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # Документация 2 | 3 | - [Настройка рабочего окружения](setup.md) 4 | - [Как сдавать задачи](ci.md) 5 | - [Style Guide](style_guide.md) 6 | - [Полезные ссылки](links.md) 7 | - [Установка Docker](docker.md) 8 | - [FAQ](faq.md) 9 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Отказ от ответственности 4 | 5 | Единое рабочее окружение позволяет обеспечить предсказуемое поведение сборки и тестов на ваших локальных машинах. 6 | 7 | Работая в окружении, отличном от описанного ниже, вы **лишаетесь техподдержки**. 8 | 9 | ## Первые шаги 10 | 11 | Пожалуйста, выполняйте описанные шаги в точности так, как они описаны! 12 | 13 | ### Шаг 0 14 | 15 | Установите `git`: 16 | ```shell 17 | sudo apt-get install git 18 | ``` 19 | 20 | ### Шаг 1 21 | 22 | Заведите в удобном месте новую директорию для работы с курсом: 23 | 24 | ```shell 25 | # Можно выбрать произвольное имя, но 26 | # 1) полный путь к этой директории не должен содержать пробелов, 27 | # 2) в именах директорий используйте только латиницу. 28 | mkdir workspace 29 | ``` 30 | 31 | Директория должна быть пустой! 32 | 33 | 34 | ### Шаг 2 35 | 36 | Клонируйте в эту директорию мой репозиторий: 37 | 38 | ```shell 39 | cd workspace 40 | git clone https://github.com/Maxsmile123/Algorithms-And-DataStructure-Course.git 41 | ``` 42 | 43 | Репозиторий курса должен оказаться в собственной поддиректории: 44 | 45 | ``` 46 | workspace 47 | | 48 | +--- Algorithms-And-DataStructure-Course 49 | ``` 50 | 51 | ### Шаг 3 52 | 53 | Перейдите в директорию с репозиторием курса и подтяните необходимые репозиторию [сабмодули git](https://git-scm.com/book/en/v2/Git-Tools-Submodules): 54 | 55 | ```shell 56 | mv Algorithms-And-DataStructure-Course algo-2025 57 | cd algo-2025 58 | git submodule init 59 | git submodule update --remote --merge 60 | ``` 61 | 62 | ### Шаг 4 63 | 64 | На этом шаге нужно подготовить зависимости курса. 65 | 66 | Есть два варианта: 67 | * **Рекомендуемый**: вы разворачиваете докер-контейнер со всеми необходимыми зависимостями и работаете в нем. 68 | * Вы самостоятельно устанавливаете необходимые зависимости в хост-систему. В этом случае вы **лишаетесь техподдержки**. 69 | 70 | 71 | #### Докер 72 | 73 | Следуйте [инструкции](/docs/docker.md). 74 | 75 | Репозиторий курса в контейнере находится в директории `/workspace`. 76 | 77 | Обратите внимание: путь `/workspace` в контейнере зафиксирован и не зависит от того, как вы назвали директорию в хост-системе на первом шаге! 78 | 79 | ### Дальнейшие шаги инструкции выполняйте **внутри контейнера**. 80 | 81 | #### Ручная установка 82 | 83 | Если вы решили пойти по этому пути, то убедитесь, что у вас свежий стабильный 64-битный Linux. 84 | 85 | Установите [необходимые зависимости](/docker/image/install_deps.sh). 86 | 87 | ### Шаг 5 88 | 89 | Теперь нужно установить 📎 [консольный клиент Clippy](https://github.com/Maxsmile123/Clippy) для работы с курсом. 90 | С его помощью вы будете запускать тесты и сдавать задачи. 91 | 92 | Перейдите в корень репозитория. 93 | 94 | Запустите установку клиента: 95 | ```shell 96 | bash ./install_client.sh 97 | ``` 98 | В ходе установки разрешите добавить `clippy` в `PATH`. 99 | 100 | После завершения установки скрипт запустит клиент с тестовой командой `hi`. 101 | 102 |  **Обновите PATH** в текущей сессии 103 | ```shell 104 | source ~/.bashrc 105 | ``` 106 | (или перезапустите shell) 107 | 108 | Проверьте, что `clippy` теперь доступен: 109 | ```shell 110 | clippy hi 111 | ``` 112 | 113 | ### Шаг 6 114 | 115 | Теперь у вас есть клиент для работы с курсом, он называется `clippy`. 116 | 117 | Выполните команду: 118 | 119 | ```shell 120 | # Генерируем файлы для сборки проекта курса 121 | clippy cmake 122 | ``` 123 | 124 | #### Работа с клиентом 125 | 126 | Самые полезные команды: 127 | 128 | - `cmake` – перегенерировать файлы сборки (`cmake --clean` – начисто) 129 | - `test` – запустить тесты текущей задачи 130 | - `validate` – проверить код текущей задачи на качество кода и запрещенные паттерны 131 | 132 | ### Шаг 7 133 | 134 | Настройте IDE для комфортной работы с курсом: Clion или VScode. 135 | 136 | Этот шаг – не обязательный, но рекомендуемый: вы будете много перемещаться по коду с помощью `Go To` и выполнять автоматические рефакторинги. 137 | 138 | В любом случае, к настройке IDE можно будет вернуться позже. 139 | 140 | Обратите внимание: сборка проекта курса через Clippy и сборка через IDE работают **независимо** друг от друга! 141 | 142 | ### Шаг 8 143 | 144 | Прочтите [Как сдавать задачи](/docs/ci.md). 145 | 146 | ### Шаг 9 147 | 148 | Решите задачу [`A+B`](../tasks/tutorial/aplusb/) 149 | -------------------------------------------------------------------------------- /docs/style_guide.md: -------------------------------------------------------------------------------- 1 | ## C++ style guide 2 | 3 | ### Оформление 4 | 1. Отступ - 2 или 4 пробела (используйте одинаковую ширину отступов во всей программе). Символы табуляции использовать для отступов запрещено. 5 | Настройка в Visual Studio 2019: Средства → Параметры → Текстовый редактор → С/C++ → Табуляция → Галочка возле "Вставлять пробелы". 6 | 2. Отступами выделяются: тела функций, структур/классов, вложенных блоков. 7 | 3. Бинарные операторы отбиваются пробелами с двух сторон, после унарных пробелы не ставятся: `a += b + -c`. 8 | 4. Пробелы после открывающей скобки и перед закрывающей скобкой не ставятся: `f(1, (2 + 3))`. Закрывающая скобка должна идти на той же строке, что и последнее выражение. 9 | 5. Максимальная длина строки - 120 символов. 10 | 6. Перед `;` пробел не ставится. После `;` в `for` ставится пробел. 11 | 7. Пустые блоки записываются как `{}` (а не `;`). 12 | 8. Открывающая `{` пишется на той же строке, что и начало блока (`if`, `while`, `for`, объявление функции). 13 | 9. `else` пишется на той же строке, что и закрывающая `}` от `if`: 14 | ```cpp 15 | if (...) { 16 | ... 17 | } else { 18 | ... 19 | } 20 | ``` 21 | 10. Однострочные комментарии отделяются от кода двумя пробелами и начинаются с пробела. 22 | 11. Пробелы в конце строки запрещены. 23 | 12. Файл должен заканчиваться переводом строки. 24 | 13. В range-based for двоеточие обрамляется пробелами. 25 | 14. В начале/конце блока, после `public`/`private`/`protected` пустые строки не ставятся. 26 | 15. Перед объявлением функции/структуры/класса пустая строка обязательна. 27 | 16. Секции `#include` и `using` должны быть упорядочены по алфавиту: 28 | ```cpp 29 | // плохо 30 | #include 31 | #include 32 | ``` 33 | ```cpp 34 | // хорошо 35 | #include 36 | #include 37 | ``` 38 | ```cpp 39 | // плохо 40 | using std::vector; 41 | using std::cin; 42 | using std::cout; 43 | ``` 44 | ```cpp 45 | // хорошо 46 | using std::cin; 47 | using std::cout; 48 | using std::vector; 49 | ``` 50 | 17. Имя шаблона и параметр шаблона не должны разделяться пробелом: 51 | ```cpp 52 | vector v1; // плохо 53 | vector v2; // хорошо 54 | ``` 55 | 18. Каждая переменная объявляется на собственной строке: 56 | ```cpp 57 | int a = 1, b = 2; // плохо 58 | 59 | int a = 1; // хорошо 60 | int b = 2; 61 | ``` 62 | 63 | ### Именование 64 | Правила наименования одни из самых важных, т. к. они помогают сразу определить какую сущность они определяют: тип, переменная, функция, константа, без необходимости поиска определения сущности. Также хорошие имена дают подсказки о работе вашего кода без необходимости каждый раз писать поясняющий комментарий. 65 | 66 | В первую очередь вы должны повышать удобство "читателей" вашего кода, а не "писателей", т. к. зачастую код пишется один раз и переиспользуется или модифицируется много раз разными людьми. И как правило С++ разработчикам в настоящих проектах в основном приходится читать очень много кода, из-за чего следование правилам оформления повышает эффективность всех. 67 | 68 | Используйте имена, которые описывают цель или сущность объекта. Не экономьте много символов на имена. Намного важнее, чтобы код был очевиден новым читателям с первого прочтения. Не используйте аббревиатуры, которые известны только вам. Не испульзуйте аббревиатуры, сделанные удалением символов из слов. Как правило, аббревиатура допустима, если она общеизвестна (DFS, BFS, HTTP, TCP, JSON, XML, RPC). 69 | Однобуквенные имена могут быть использованы в коротких очевидных функциях, например из 5 строк. Примеры: 70 | * Имя `n` для размера массива 71 | * Имя `i`, `j`, `k` для счетчиков цикла 72 | * Имя `it` для итератора 73 | * Имя `e` для исключения 74 | * Имя `T` для типа шаблона 75 | 76 | 1. Имена переменных и полей классов пишутся в нижнем регистре, слова разделяются символом подчеркивания. Приватные поля классов дополнительно имеют один символ подчеркивания в суффиксе: 77 | ```cpp 78 | int TableName; // плохо 79 | int table_name; // хорошо 80 | 81 | std::vector vec; // плохо 82 | std::vector arr; // плохо 83 | std::vector scores; // хорошо 84 | std::vector indices; // хорошо 85 | ``` 86 | 87 | 2. Не используйте транслит в именах: 88 | ```cpp 89 | size_t dlina_massiva; // плохо 90 | size_t length; // хорошо 91 | ``` 92 | 3. Для переменных-счётчиков не следует использовать имена `document_number`, `number_document`, `documents_count`, `documents_number`, `count_document` из-за неграмотности и неоднозначности. Для числа элементов (скажем документов) можно использовать `number_documents` или `document_count`. Для функции, долго и явно подсчитывающей это число, подойдет `CountDocuments()`. 93 | 94 | 4. Переменные, определенные с ключевым словом constexpr и const, и чье значение определенно на все протяжение работы программы, пишутся с большими буквами, слова разделяются символом подчеркивания. 95 | ```cpp 96 | const int MAX_SIZE = 100'000; 97 | const int MODULO = 1'000'000'000 + 7; 98 | ``` 99 | 100 | 101 | 5. Имена типов начинаются с большой буквы, каждое слово пишется слитно и с большой буквы: MyClass.
102 | Под именами типов имеются ввиду следующие сущности: классы, структуры, type alias, enum, параметры шаблонов. Пример: 103 | ```cpp 104 | class UrlTable { ... 105 | class UrlTableTester { ... 106 | struct UrlTableProperties { ... 107 | 108 | // typedefs 109 | typedef hash_map PropertiesMap; 110 | 111 | // using aliases 112 | using PropertiesMap = hash_map; 113 | 114 | // enums 115 | enum class UrlTableError { ... 116 | ``` 117 | 118 | 6. По умолчанию, имена функций и методов начинаются с большой буквы, каждое слово начинается с большой буквы. Имя функции должно отражать действия или задачу, которую функция делает и решает соответственно. 119 | ```cpp 120 | AddTableEntry() 121 | DeleteUrl() 122 | OpenFileOrDie() 123 | ``` 124 | 125 | Некоторые методы могут называться, как переменные. Как правило, это методы для соответствия API стандартной библиотеки: `begin()/end()`, `size()`, `empty()`, `insert()`, `push_back()`. 126 | 127 | 128 | Функциям, которые возвращают bool, лучше давать имена, начинающиеся на Is или Has. 129 | 130 | 131 | ```cpp 132 | // плохо 133 | bool graph_connected() { ... } 134 | 135 | // хорошо 136 | bool IsConnectedGraph() { ... } 137 | 138 | // плохо 139 | bool element(int n) { ... } 140 | 141 | // хорошо 142 | bool HasElement(int n) { ... } 143 | ``` 144 | 145 | ### `std::vector` 146 | 1. Если требуется создать вектор размера `n`, воспользуйтесь специальным конструктором. 147 | ```cpp 148 | // плохо 149 | vector v; 150 | v.resize(n); 151 | ``` 152 | ```cpp 153 | // хорошо 154 | vector v(n); 155 | ``` 156 | 157 | ### Циклы 158 | 1. Если требуется перебрать элементы коллекции, предпочитайте range-based for. Он выглядит лаконичнее и легче читается. 159 | ```cpp 160 | // плохо 161 | for (size_t i = 0; i < neighbors_list[vertex].size(); ++i) { 162 | if (!(visited[neighbors_list[vertex][i]])) { 163 | dfs(neighbors_list[vertex][i], visited); 164 | } 165 | } 166 | ``` 167 | ```cpp 168 | // хорошо 169 | for (const int& neighbor_vertex : neighbors_list[vertex]) { 170 | if (!(visited[neighbor_vertex])) { 171 | dfs(neighbor_vertex, visited); 172 | } 173 | } 174 | ``` 175 | 176 | ### Функции 177 | Функции нужны 178 | - для избежания дублирования кода; 179 | - для того, чтобы можно было их переиспользовать много раз в различных проектах. 180 | 181 | 1. Порядок аргументов функции: сначала входные параметры (по значению либо константой ссылке), затем выходные (по указателю или ссылке). 182 | 2. Передавайте аргументы в функции по константной ссылке везде, где это уместно: 183 | ```cpp 184 | // плохо 185 | void PrintVector(vector v) { 186 | ... 187 | } 188 | ``` 189 | ```cpp 190 | // хорошо 191 | void PrintVector(const vector& v) { 192 | ... 193 | } 194 | ``` 195 | 3. Не создавайте функции с избыточным числом аргументов. Например, в функцию, печатающую вектор, не нужно передавать размер вектора. 196 | ```cpp 197 | // плохо 198 | void PrintVector(const vector& v, int n) { 199 | for (size_t i = 0; i < n; ++i) { 200 | cout << v[i] << " "; 201 | } 202 | } 203 | ``` 204 | ```cpp 205 | // хорошо 206 | void PrintVector(const vector& v) { 207 | // Используйте v.size(), чтобы получить длину вектора 208 | for (size_t i = 0; i < v.size(); ++i) { 209 | cout << v[i] << " "; 210 | } 211 | } 212 | ``` 213 | 4. Пусть у вас есть вектор `vector way`, хранящий путь, 214 | и требуется написать функцию, которая печатает этот путь. 215 | ```cpp 216 | // плохо 217 | void PrintWay(const vector& way) { 218 | for (size_t i = 0; i < way.size(); ++i) { 219 | cout << way[i] << " "; 220 | } 221 | } 222 | ``` 223 | ```cpp 224 | // хорошо 225 | void PrintVector(const vector& v) { 226 | for (size_t i = 0; i < v.size(); ++i) { 227 | cout << v[i] << " "; 228 | } 229 | } 230 | 231 | // или 232 | 233 | void PrintVector(const vector& v) { 234 | for (size_t i = 0; i < v.size(); ++i) { 235 | cout << v[i] << " "; 236 | } 237 | } 238 | 239 | void PrintWay(const vector& way) { 240 | PrintVector(way); 241 | } 242 | ``` 243 | Плохой вариант плох тем, что теряется свойство переиспользования у функции: будет неуместно использовать `print_way` в другом проекте, где, скажем, нужно будет распечатать вектор оценок. 244 | 245 | ### Прочее 246 | 1. Необходимо явно подключать заголовочные файлы, в которых объявляются используемые функции, классы и т. д. Запрещено явно подключать один и тот же заголовочный файл дважды. 247 | 248 | 2. В качестве логических операторов следует использовать `&&`, `||` и т. д. Их аналоги `and`, `or`, ... запрещены. 249 | 250 | 3. Запрещено использовать приведение типов в стиле C - следует использовать `static_cast`. 251 | 252 | 4. Конструктор от одного аргумента должен быть объявлен `explicit`. 253 | 254 | 5. При объявлении виртуальной функции следует использовать один и только один из спецификаторов `virtual`, `final`, `override`. 255 | 256 | 6. При объявлении переменной спецификаторы `static`/`extern`/... идут перед именем типа. 257 | 258 | 7. Везде, где это возможно, используйте префиксный инкремент и декремент: 259 | ```cpp 260 | // плохо 261 | i++; 262 | it--; 263 | ``` 264 | ```cpp 265 | // хорошо 266 | ++i; 267 | --it; 268 | ``` 269 | 270 | 8. Пишите код так, чтобы не было предупреждений (warnings) компилятора. Это нужно по двум причинам: 271 | - Обычно компилятор выдаёт предупреждения по делу, на те места, где скрыта потенциальная ошибка. 272 | - Если игнорировать "неважные" предупреждения, их может много накопиться и вы не заметите действительно важных. 273 | В частности, избегайте сравнений знаковых (`int`) и беззнаковых (`size_t`) переменных. 274 | ```cpp 275 | // плохо 276 | for (int i = 0; i < v.size(); ++i) { 277 | ... 278 | } 279 | ``` 280 | ```cpp 281 | // хорошо 282 | for (size_t i = 0; i < v.size(); ++i) { 283 | ... 284 | } 285 | ``` 286 | 287 | ### Примеры и проверка 288 | Как делать не надо: 289 | ```cpp 290 | #include 291 | #include 292 | #include 293 | 294 | int extern q; 295 | 296 | class A { 297 | virtual void f(void) final override; 298 | A(int); 299 | }; 300 | 301 | int main(void){ 302 | 303 | bool a=1 or (int)(2); 304 | 305 | if (1) { 306 | f ( 2); 307 | } 308 | else { 309 | } 310 | if (2); 311 | &static_cast(2); 312 | char *c; 313 | c [1]; 314 | c,a; //comment 315 | for (auto i:std::vector()); 316 | for (;i<1;){} 317 | return 0; 318 | } 319 | ``` 320 | 321 | ### `Важно` 322 | 323 | *Действия пользователей не могут рассматриваться в качестве причины сбоев. Все пользователи, кроме сертифицированных, специально обученных специалистов, могут делать с софтом все что заблагорассудится. Мы должны ожидать от них любого поведения. Если какие-то действия пользователей приводят к сбою, налицо недоработка в софте.* 324 | 325 | ### `Запрещена чёрная магия!` 326 | Мы живём во времена инквизиции, дорогие друзья. Код должен быть понятен мне и вашим коллегам. 327 | -------------------------------------------------------------------------------- /install_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | chmod +x client/install.py 4 | 5 | if ( which dirname ) && ( which realpath ); then 6 | PROJECT_ROOT="$(dirname "$(realpath "$0")")" 7 | cd "${PROJECT_ROOT}" 8 | fi 9 | 10 | git submodule update --remote --merge 11 | client/install.py --alias clippy 12 | -------------------------------------------------------------------------------- /library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | # -------------------------------------------------------------------- 4 | 5 | # Offline mode (uncomment next line to enable) 6 | # set(FETCHCONTENT_FULLY_DISCONNECTED ON) 7 | 8 | # set(FETCHCONTENT_QUIET OFF) 9 | 10 | # -------------------------------------------------------------------- 11 | 12 | # Libraries 13 | 14 | # -------------------------------------------------------------------- 15 | 16 | # fmt with print 17 | 18 | project_log("FetchContent: fmt") 19 | 20 | FetchContent_Declare( 21 | fmt 22 | GIT_REPOSITORY https://github.com/fmtlib/fmt.git 23 | GIT_TAG 87c066a35b7cc70bb7d438a543c8b49b577e61f4 24 | ) 25 | FetchContent_MakeAvailable(fmt) 26 | 27 | # -------------------------------------------------------------------- 28 | 29 | # Google test 30 | 31 | project_log("FetchContent: Google test") 32 | 33 | FetchContent_Declare( 34 | googletest 35 | GIT_REPOSITORY https://github.com/google/googletest.git 36 | GIT_TAG f8d7d77 37 | ) 38 | FetchContent_MakeAvailable(googletest) 39 | 40 | # -------------------------------------------------------------------- 41 | 42 | # Google Benchmark 43 | 44 | project_log("FetchContent: Google Benchmark") 45 | 46 | FetchContent_Declare( 47 | googlebenchmark 48 | GIT_REPOSITORY https://github.com/google/benchmark.git 49 | GIT_TAG 015d1a0 50 | ) 51 | 52 | FetchContent_MakeAvailable(googlebenchmark) 53 | 54 | # -------------------------------------------------------------------- 55 | 56 | # Memory allocation 57 | 58 | project_log("FetchContent: mimalloc") 59 | 60 | FetchContent_Declare( 61 | mimalloc 62 | GIT_REPOSITORY https://github.com/microsoft/mimalloc 63 | GIT_TAG af21001 64 | 65 | ) 66 | FetchContent_MakeAvailable(mimalloc) 67 | 68 | 69 | -------------------------------------------------------------------------------- /tasks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Topics 2 | 3 | add_subdirectory(tree) 4 | add_subdirectory(abstract) 5 | add_subdirectory(vector) 6 | add_subdirectory(lists) 7 | add_subdirectory(tutorial) 8 | add_subdirectory(bash) 9 | 10 | -------------------------------------------------------------------------------- /tasks/README.md: -------------------------------------------------------------------------------- 1 | # Задачи 2 | 3 | - [`A+B`](tutorial) - тестовая задача. 4 | - [`Bash`](bash) – Shell scripting 5 | - [`Tree`](tree) – бинарные деревья поиска, файловая система: `std::map` 6 | - [`Vector`](vector) – динамический массив: `std::vector` 7 | - [`Lists`](lists) – линейные списки: `std::list`, `std::forward_list` 8 | - [`Deque`](abstract) – абстрактные структуры данных: `std::deque`, `std::stack`, `std::queue` 9 | -------------------------------------------------------------------------------- /tasks/abstract/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | add_subdirectory(deque) 4 | add_subdirectory(queue) 5 | add_subdirectory(stack) 6 | -------------------------------------------------------------------------------- /tasks/abstract/deque/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(deque.hpp) 3 | end_task() 4 | -------------------------------------------------------------------------------- /tasks/abstract/deque/deque.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/deque/deque.hpp -------------------------------------------------------------------------------- /tasks/abstract/deque/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/deque/image.png -------------------------------------------------------------------------------- /tasks/abstract/deque/readme.md: -------------------------------------------------------------------------------- 1 | # Дэк 2 | 3 | Реализуйте в свободной форме std::deque, использующий архитектуру с чанками: 4 | ![Alt text](image.png) 5 | 6 | Важно, что у вас отсутствует инвалидация итераторов/указателей при реалокации дэка. 7 | 8 | Правило пяти следует соблюсти в данном контейнере. 9 | 10 | Реализуйте следующие методы: 11 | - **push_back** // Вставить в конец дэка, O(1) 12 | - **push_front** // Вставить в начало дэка, O(1) 13 | - **pop_back** // Удалить один элемент из конца дэка, O(1) 14 | - **pop_front** // Удалить один элемент из начало дэка, O(1) 15 | - **back** // Получить доступ к последнему элементу дэка, O(1) 16 | - **front** // Получить доступ к первому элементу дэка, O(1) 17 | - **operator []** // Получить доступ к произвольному элементу дэка, O(1) 18 | -------------------------------------------------------------------------------- /tasks/abstract/deque/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint_files": ["deque.hpp"], 3 | "submit_files": ["deque.hpp"], 4 | "forbidden": [ 5 | { 6 | "patterns": [ 7 | "std::deque" 8 | ], 9 | "hint": "Don't use std::deque" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tasks/abstract/deque/tests/dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/deque/tests/dump -------------------------------------------------------------------------------- /tasks/abstract/queue/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(queue.hpp) 3 | end_task() 4 | -------------------------------------------------------------------------------- /tasks/abstract/queue/queue.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/queue/queue.hpp -------------------------------------------------------------------------------- /tasks/abstract/queue/readme.md: -------------------------------------------------------------------------------- 1 | # Очередь 2 | 3 | Реализуйте в свободной форме std::queue, согласно паттерну проектирования "адаптер". 4 | 5 | В шаблоных параметрах очередь принимает, помимо хранимого типа, контейнер, на основе которого будет построена. 6 | 7 | API очереди: 8 | - **push** - вставить в конец очереди 9 | - **pop** - удалить элемент из начала очереди 10 | - **front** - получить элемент из начала очереди -------------------------------------------------------------------------------- /tasks/abstract/queue/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint_files": ["queue.hpp"], 3 | "submit_files": ["queue.hpp"], 4 | "forbidden": [ 5 | { 6 | "patterns": [ 7 | "std::queue" 8 | ], 9 | "hint": "Don't use std::queue" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tasks/abstract/queue/tests/dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/queue/tests/dump -------------------------------------------------------------------------------- /tasks/abstract/readme.md: -------------------------------------------------------------------------------- 1 | # Абстрактные структуры данных 2 | 3 | - [Дэк](deque) 4 | - [Стек](stack) 5 | - [Очередь](queue) -------------------------------------------------------------------------------- /tasks/abstract/stack/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(stack.hpp) 3 | end_task() 4 | -------------------------------------------------------------------------------- /tasks/abstract/stack/readme.md: -------------------------------------------------------------------------------- 1 | # Стэк 2 | 3 | Реализуйте в свободной форме std::stack, согласно паттерну проектирования "адаптер". 4 | 5 | В шаблоных параметрах стэк принимает, помимо хранимого типа, контейнер, на основе которого будет построена. 6 | 7 | API очереди: 8 | - **push** - вставить в конец стэка 9 | - **pop** - удалить элемент из конца стэка 10 | - **top** - получить элемент c вершины стека -------------------------------------------------------------------------------- /tasks/abstract/stack/stack.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/stack/stack.hpp -------------------------------------------------------------------------------- /tasks/abstract/stack/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint_files": ["stack.hpp"], 3 | "submit_files": ["stack.hpp"], 4 | "forbidden": [ 5 | { 6 | "patterns": [ 7 | "std::stack" 8 | ], 9 | "hint": "Don't use std::stack" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tasks/abstract/stack/tests/dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/abstract/stack/tests/dump -------------------------------------------------------------------------------- /tasks/bash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | add_subdirectory(bash) -------------------------------------------------------------------------------- /tasks/bash/bash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | end_task() 3 | -------------------------------------------------------------------------------- /tasks/bash/bash/readme.md: -------------------------------------------------------------------------------- 1 | # Скрипты на Bash и Python 2 | 3 | В этой задаче познакомимся с языком Bash, а также сравним его с Python. 4 | 5 | ## Bash 6 | 7 | Bash - язык скриптов для операционной системы. На нём пишут набор команд терминала, которые выполняются построчно. Классические bash скрипты: [`установка зависимостей`](../../install_deps.sh), [`установка программ`](../../install_client.sh), работа с docker-контейнерами и etc. 8 | 9 | ## Изучение 10 | 11 | Bash имеет довольно неприятный синтаксис. Изучить его предлагается двумя способами: 12 | 1) Интерактивный [tutorial](https://www.learnshell.org/) 13 | 2) Цикл статьей на [habr](https://habr.com/ru/companies/ruvds/articles/325522/) 14 | 15 | 16 | ## Python 17 | 18 | Python - язык общего назначения. Однако, используя библиотку [`os`](https://docs.python.org/3/library/os.html) можно выполнять те же действия, что и в Bash. 19 | 20 | В проектах применяют оба подхода, но чаще встречается Bash скрипты на случай, если на машине не установлен интерпретатор Python. 21 | 22 | ## References 23 | 24 | * [`Bash vs. Python: For Modern Shell Scripting`](https://levelup.gitconnected.com/bash-vs-python-for-modern-shell-scripting-c1d3d79c3622) 25 | * [`Bash manual GNU`](https://www.gnu.org/software/bash/manual/bash.html) 26 | 27 | ## Задание 28 | 29 | Решите задачу своего [варианта](variant.md), написав реализацию в файлах `solution.sh` и `solution.py`. 30 | 31 | Скрипт нужно написать как на Bash, так и на Python. 32 | 33 | Интерфейс скриптов `должен быть одинаковым!`. 34 | Входные данные передаются в виде аргументов командной строки. 35 | 36 | Результат действия скриптов должен быть одинаковым. 37 | 38 | Добавьте флаг -h или --help, который выведёт краткую инфорацию о том, как работать с скриптом. 39 | 40 | ## Реализация 41 | 42 | ### Аргументы командной строки 43 | 44 | В Python стоит использовать библиотку [`Argparse`](https://docs.python.org/3/library/argparse.html) 45 | 46 | В Bash делается [так](https://www.squash.io/passing-parameters-to-scripts-in-bash/) 47 | 48 | Опции, как правило, бывают полные (`--help`), а бывают аналогичные, но краткие (`-h`). Приветствуется поддержка этого в Вашем скрипте. 49 | 50 | ### Обработка ошибок 51 | 52 | Если ваш скрипт предполагает обязательные входные данные, а пользователь их не ввёл - стоит напечатать USAGE, i.e. -h и закончить программу. 53 | 54 | Никаких лишних сообщений об ошибках писать не стоит. 55 | 56 | ### Формат справки 57 | 58 | `--help` имеет негласный шаблон. Используйте как у `ls`: 59 | ```shell 60 | Usage: ls [OPTION]... [FILE]... # USAGE 61 | List information about the FILEs (the current directory by default). # What about 62 | Sort entries alphabetically if none of -cftuvSUX nor --sort is specified. # Specific 63 | 64 | Mandatory arguments to long options are mandatory for short options too. # Flags 65 | -a, --all do not ignore entries starting with . 66 | -A, --almost-all do not list implied . and .. 67 | --author with -l, print the author of each file 68 | -b, --escape print C-style escapes for nongraphic char 69 | ... 70 | ``` -------------------------------------------------------------------------------- /tasks/bash/bash/solution.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/bash/bash/solution.py -------------------------------------------------------------------------------- /tasks/bash/bash/solution.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | -------------------------------------------------------------------------------- /tasks/bash/bash/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [], 3 | "lint_files": [], 4 | "submit_files": ["solution.sh", "solution.py"], 5 | "forbidden": [] 6 | } 7 | -------------------------------------------------------------------------------- /tasks/lists/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | add_subdirectory(list) 4 | add_subdirectory(forward) 5 | -------------------------------------------------------------------------------- /tasks/lists/forward/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(forward_list.hpp) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | add_task_test(stress_tests tests/stress.cpp) 5 | end_task() 6 | -------------------------------------------------------------------------------- /tasks/lists/forward/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class ListIsEmptyException : std::exception { 7 | public: 8 | explicit ListIsEmptyException(const std::string& text) : error_message_(text) { 9 | } 10 | 11 | const char* what() const noexcept override { 12 | return error_message_.data(); 13 | } 14 | 15 | private: 16 | std::string_view error_message_; 17 | }; -------------------------------------------------------------------------------- /tasks/lists/forward/forward_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | class ForwardList { 12 | private: 13 | class Node { 14 | friend class ForwardListIterator; 15 | friend class ForwardList; 16 | /*???*/ 17 | 18 | private: 19 | /*???*/ 20 | }; 21 | 22 | public: 23 | class ForwardListIterator { 24 | public: 25 | /*???*/ 26 | 27 | private: 28 | explicit ForwardListIterator(const Node*) { 29 | // Not implemented 30 | } 31 | 32 | private: 33 | Node* current_; 34 | }; 35 | 36 | public: 37 | ForwardList() { 38 | // Not implemented 39 | } 40 | 41 | explicit ForwardList(size_t /*sz*/) { 42 | // Not implemented 43 | } 44 | 45 | ForwardList(const std::initializer_list& /*values*/) { 46 | // Not implemented 47 | } 48 | 49 | ForwardList(const ForwardList& /*other*/) { 50 | // Not implemented 51 | } 52 | 53 | ForwardList& operator=(const ForwardList& /*other*/) { 54 | std::abort(); // Not implemented 55 | } 56 | 57 | ForwardListIterator Begin() const noexcept { 58 | std::abort(); // Not implemented 59 | } 60 | 61 | ForwardListIterator End() const noexcept { 62 | std::abort(); // Not implemented 63 | } 64 | 65 | inline T& Front() const { 66 | std::abort(); // Not implemented 67 | } 68 | 69 | inline bool IsEmpty() const noexcept { 70 | std::abort(); // Not implemented 71 | } 72 | 73 | inline size_t Size() const noexcept { 74 | std::abort(); // Not implemented 75 | } 76 | 77 | void Swap(ForwardList& /*a*/) { 78 | // Not implemented 79 | } 80 | 81 | void EraseAfter(ForwardListIterator /*pos*/) { 82 | // Not implemented 83 | } 84 | 85 | void InsertAfter(ForwardListIterator /*pos*/, const T& /*value*/) { 86 | // Not implemented 87 | } 88 | 89 | ForwardListIterator Find(const T& /*value*/) const { 90 | std::abort(); // Not implemented 91 | } 92 | 93 | void Clear() noexcept { 94 | // Not implemented 95 | } 96 | 97 | void PushFront(const T& /*value*/) { 98 | // Not implemented 99 | } 100 | 101 | void PopFront() { 102 | // Not implemented 103 | } 104 | 105 | ~ForwardList() { 106 | // Not implemented 107 | } 108 | 109 | private: 110 | /*???*/ 111 | }; 112 | 113 | namespace std { 114 | // Global swap overloading 115 | template 116 | void swap(ForwardList& a, ForwardList& b) { // NOLINT 117 | a.Swap(b); 118 | } 119 | } // namespace std 120 | -------------------------------------------------------------------------------- /tasks/lists/forward/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/lists/forward/images/image.png -------------------------------------------------------------------------------- /tasks/lists/forward/readme.md: -------------------------------------------------------------------------------- 1 | # Односвязный список 2 | 3 | ## Пререквизиты 4 | 5 | - [lists/list](/tasks/lists/list) 6 | --- 7 | 8 | В этой задаче напишем свой [std::forward_list](https://en.cppreference.com/w/cpp/container/forward_list). 9 | 10 | Это список, у которого узлы связаны только одним указателем. Ходить в нём назад нельзя. 11 | 12 | ![Alt text](./images/image.png) 13 | 14 | 15 | ## Мотивация 16 | 17 | Каждая Node хранит на один указатель меньше. Это позволяет занимать меньше памяти когда вам не нужно ходить в обе стороны. 18 | 19 | **Обладает такими же свойствами, как и обычный двусвязный список:** 20 | - Вставка элемента происходит за `O(1)`. 21 | - Поиск элемента происходит за `O(N)`. 22 | - Удаление элемента за `O(1)`. 23 | 24 | ## Что урезалось? 25 | Раз мы не можем ходить назад, то мы не сможем быстро получить последний элемент списка. `PushBack` и `PopBack` также становяться недоступны. 26 | 27 | Помимо этого, все операции вставки/удаления теперь доступны только `после` указанного итератора. 28 | 29 | У итераторов запрещены операции декремента. 30 | 31 | 32 | ## Задание 33 | 34 | Реализуйте [односвязный список](forward_list.hpp) и итераторы к нему. 35 | 36 | ### Указания к реализации 37 | 38 | Старайтесь избежать копипасты из [lists/list](../list/). Декомпозируйте в общие элементы, наследуйте. 39 | 40 | **В публичное API не стоит добавлять новых методов!** 41 | 42 | **В публичном API не должно быть класса `Node`!** 43 | 44 | ## Примечание 45 | 46 | В Стресс-тесте сравнится по скорости ваша реализация с `std::forward_list`. -------------------------------------------------------------------------------- /tasks/lists/forward/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": ["forward_list.hpp", "exceptions.hpp"], 18 | "submit_files": ["forward_list.hpp", "exceptions.hpp"], 19 | "forbidden": [ 20 | { 21 | "patterns": [ 22 | "Not implemented" 23 | ], 24 | "hint": "You should implement this part" 25 | }, 26 | { 27 | "patterns": [ 28 | "std::list", 29 | "std::forward_list", 30 | "std::vector", 31 | "std::forward_list" 32 | ], 33 | "hint": "Don't use STL containers" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tasks/lists/forward/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../forward_list.hpp" 9 | 10 | void ConstructRandomList(ForwardList& list, int sz) { 11 | std::random_device rd; 12 | std::mt19937 mt(rd()); 13 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 14 | int random_key; 15 | while(sz) { 16 | random_key = dist(mt); 17 | list.PushFront(random_key); 18 | --sz; 19 | } 20 | } 21 | 22 | void ConstructRandomList(std::forward_list& list, int sz) { 23 | std::random_device rd; 24 | std::mt19937 mt(rd()); 25 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 26 | int random_key; 27 | while(sz) { 28 | random_key = dist(mt); 29 | list.push_front(random_key); 30 | --sz; 31 | } 32 | } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | void BM_CustomListPushFront(benchmark::State& state) { 36 | ForwardList list; 37 | for (auto _ : state) { 38 | ConstructRandomList(list, state.range(0)); 39 | } 40 | state.SetComplexityN(state.range(0)); 41 | } 42 | 43 | void BM_StdListPushFront(benchmark::State& state) { 44 | std::forward_list list; 45 | for (auto _ : state) { 46 | ConstructRandomList(list, state.range(0)); 47 | } 48 | state.SetComplexityN(state.range(0)); 49 | } 50 | 51 | void BM_CustomListMiddleInsert(benchmark::State& state) { 52 | ForwardList list; 53 | ConstructRandomList(list, 100); 54 | auto it = list.Begin(); 55 | std::advance(it, 50); 56 | for (auto _ : state) { 57 | for (int i = 0; i < state.range(0); ++i){ 58 | list.InsertAfter(it, 50); 59 | } 60 | } 61 | state.SetComplexityN(state.range(0)); 62 | } 63 | 64 | void BM_StdListMiddleInsert(benchmark::State& state) { 65 | std::forward_list list; 66 | ConstructRandomList(list, 100); 67 | auto it = list.begin(); 68 | std::advance(it, 50); 69 | for (auto _ : state) { 70 | for (int i = 0; i < state.range(0); ++i){ 71 | list.insert_after(it, 50); 72 | } 73 | } 74 | state.SetComplexityN(state.range(0)); 75 | } 76 | 77 | void BM_CustomListErase(benchmark::State& state) { 78 | ForwardList list; 79 | for (auto _ : state) { 80 | ConstructRandomList(list, state.range(0)); 81 | for (int64_t i = 0; i < state.range(0) - 1; ++i) { 82 | list.EraseAfter(list.Begin()); 83 | } 84 | } 85 | state.SetComplexityN(state.range(0)); 86 | } 87 | 88 | void BM_StdListErase(benchmark::State& state) { 89 | std::forward_list list; 90 | for (auto _ : state) { 91 | ConstructRandomList(list, state.range(0)); 92 | for (int64_t i = 0; i < state.range(0) - 1; ++i) { 93 | list.erase_after(list.begin()); 94 | } 95 | } 96 | state.SetComplexityN(state.range(0)); 97 | } 98 | 99 | void BM_CustomListClear(benchmark::State& state) { 100 | ForwardList list; 101 | for (auto _ : state) { 102 | ConstructRandomList(list, state.range(0)); 103 | list.Clear(); 104 | } 105 | state.SetComplexityN(state.range(0)); 106 | } 107 | 108 | void BM_StdListClear(benchmark::State& state) { 109 | std::forward_list list; 110 | for (auto _ : state) { 111 | ConstructRandomList(list, state.range(0)); 112 | list.clear(); 113 | } 114 | state.SetComplexityN(state.range(0)); 115 | } 116 | 117 | void BM_CustomListFind(benchmark::State& state) { 118 | ForwardList list; 119 | std::random_device rd; 120 | std::mt19937 mt(rd()); 121 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 122 | int random_key; 123 | for (auto _ : state) { 124 | ConstructRandomList(list, state.range(0)); 125 | random_key = dist(mt); 126 | list.Find(random_key); 127 | } 128 | state.SetComplexityN(state.range(0)); 129 | } 130 | 131 | void BM_StdListFind(benchmark::State& state) { 132 | std::forward_list list; 133 | std::random_device rd; 134 | std::mt19937 mt(rd()); 135 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 136 | int random_key; 137 | for (auto _ : state) { 138 | ConstructRandomList(list, state.range(0)); 139 | random_key = dist(mt); 140 | std::ignore = std::find(list.begin(), list.end(), random_key); 141 | } 142 | state.SetComplexityN(state.range(0)); 143 | } 144 | 145 | 146 | BENCHMARK(BM_CustomListPushFront)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 147 | BENCHMARK(BM_StdListPushFront)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 148 | BENCHMARK(BM_CustomListMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 149 | BENCHMARK(BM_StdListMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 150 | BENCHMARK(BM_CustomListErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 151 | BENCHMARK(BM_StdListErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 152 | BENCHMARK(BM_CustomListClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 153 | BENCHMARK(BM_StdListClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 154 | BENCHMARK(BM_CustomListFind)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 155 | BENCHMARK(BM_StdListFind)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 156 | 157 | 158 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/lists/forward/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../forward_list.hpp" 8 | 9 | class ListTest: public testing::Test { 10 | protected: 11 | void SetUp() override { 12 | list.PushFront(1); 13 | list.PushFront(2); 14 | list.PushFront(3); 15 | list.PushFront(4); 16 | list.PushFront(5); 17 | list.PushFront(6); 18 | list.PushFront(7); 19 | assert(list.Size() == sz); 20 | } 21 | ForwardList list; 22 | const size_t sz = 7; 23 | }; 24 | 25 | 26 | TEST(EmptyListTest, DefaultConstructor) { 27 | ForwardList list; 28 | ASSERT_TRUE(list.IsEmpty()) << "Default list isn't empty!"; 29 | } 30 | 31 | TEST(EmptyListTest, PushFrontSimple) { 32 | ForwardList list; 33 | list.PushFront(2); 34 | ASSERT_EQ(list.Size(), 1); 35 | ASSERT_EQ(list.Front(), 2); 36 | } 37 | 38 | TEST(EmptyListTest, PopFrontSimple) { 39 | ForwardList list; 40 | list.PushFront(1); 41 | list.PushFront(2); 42 | list.PopFront(); 43 | 44 | ASSERT_EQ(list.Size(), 1); 45 | ASSERT_EQ(list.Front(), 1); 46 | } 47 | 48 | TEST(EmptyListTest, PopFrontEmptyList) { 49 | ForwardList list; 50 | EXPECT_THROW({ 51 | list.PopFront(); 52 | }, ListIsEmptyException); 53 | } 54 | 55 | TEST(EmptyListTest, ConstructorSizeDefaultValues) { 56 | ForwardList list(5); 57 | ASSERT_EQ(list.Size(), 5); 58 | while (list.Size()) { 59 | ASSERT_TRUE(list.Front().empty()); 60 | list.PopFront(); 61 | } 62 | } 63 | 64 | TEST(EmptyListTest, ConstructorWithInitList) { 65 | ForwardList list{1, 2, 3, 4, 5, 6, 7, 8}; 66 | ASSERT_EQ(list.Size(), 8); 67 | int iter = 1; 68 | while (!list.IsEmpty()) { 69 | ASSERT_EQ(list.Front(), iter++); 70 | list.PopFront(); 71 | } 72 | } 73 | 74 | TEST(EmptyListTest, Swap) { 75 | ForwardList list; 76 | list.PushFront(5); 77 | 78 | ForwardList lst; 79 | lst.PushFront(15); 80 | lst.PushFront(14); 81 | 82 | size_t old_mp_size = list.Size(); 83 | size_t old_dict_size = lst.Size(); 84 | 85 | std::swap(list, lst); 86 | 87 | ASSERT_EQ(lst.Size(), old_mp_size); 88 | ASSERT_EQ(list.Size(), old_dict_size); 89 | 90 | ASSERT_EQ(lst.Front(), 5); 91 | ASSERT_EQ(list.Front(), 14); 92 | list.PopFront(); 93 | ASSERT_EQ(list.Front(), 15); 94 | } 95 | 96 | TEST_F(ListTest, CopyConstructor) { 97 | ForwardList lst = list; 98 | ASSERT_NE(&list, &lst) << "Copy constructor must do copy!\n"; 99 | ASSERT_EQ(list.Size(), lst.Size()); 100 | while (!lst.IsEmpty()) { 101 | ASSERT_EQ(list.Front(), lst.Front()); 102 | list.PopFront(); 103 | if (!list.IsEmpty()) { 104 | ASSERT_NE(list.Front(), lst.Front()); 105 | } 106 | lst.PopFront(); 107 | } 108 | } 109 | 110 | TEST_F(ListTest, CopyAssigment) { 111 | ForwardList lst; 112 | lst.PushFront(4); 113 | list = lst; 114 | ASSERT_NE(&list, &lst) << "Copy assigment must do copy!\n"; 115 | ASSERT_EQ(list.Size(), lst.Size()); 116 | while (!lst.IsEmpty()) { 117 | ASSERT_EQ(list.Front(), lst.Front()); 118 | list.PopFront(); 119 | lst.PopFront(); 120 | } 121 | } 122 | 123 | TEST_F(ListTest, SelfAssignment) { 124 | std::thread thread([&](){ 125 | list = list; 126 | }); 127 | auto future = std::async(std::launch::async, &std::thread::join, &thread); 128 | ASSERT_LT( 129 | future.wait_for(std::chrono::seconds(1)), 130 | std::future_status::timeout 131 | ) << "There is infinity loop!\n"; 132 | } 133 | 134 | TEST_F(ListTest, RangeWithIteratorPreFix) { 135 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 136 | "Distanse between begin and end iterators ins't equal size"; 137 | int iter = 7; 138 | for (auto it = list.Begin(); it != list.End(); ++it) { 139 | ASSERT_EQ(*it, iter--); 140 | } 141 | } 142 | 143 | TEST_F(ListTest, RangeWithIteratorPostFix) { 144 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 145 | "Distanse between begin and end iterators ins't equal size"; 146 | int iter = 7; 147 | for (auto it = list.Begin(); it != list.End(); it++) { 148 | ASSERT_EQ(*it, iter--); 149 | } 150 | } 151 | 152 | TEST_F(ListTest, FindFirst) { 153 | auto it = list.Begin(); 154 | ASSERT_EQ(it, list.Find(7)); 155 | } 156 | 157 | TEST_F(ListTest, FindLast) { 158 | auto it = list.Begin(); 159 | std::advance(it, 6); 160 | ASSERT_EQ(it, list.Find(1)); 161 | } 162 | 163 | TEST_F(ListTest, FindFail) { 164 | auto it = list.End(); 165 | ASSERT_EQ(it, list.Find(999)); 166 | } 167 | 168 | TEST_F(ListTest, FindAll) { 169 | auto it = list.Begin(); 170 | for (int i = 7; i >= 1; --i) { 171 | ASSERT_EQ(it++, list.Find(i)); 172 | } 173 | } 174 | 175 | TEST_F(ListTest, EraseBegin) { 176 | int first_value = *(++list.Begin()); 177 | list.EraseAfter(list.Begin()); 178 | ASSERT_EQ(list.Size(), sz - 1); 179 | ASSERT_NE(*(++list.Begin()), first_value); 180 | } 181 | 182 | TEST_F(ListTest, EraseMedium) { 183 | auto it = list.Begin(); 184 | std::advance(it, list.Size() / 2); 185 | list.EraseAfter(it); 186 | ASSERT_EQ(list.Size(), sz - 1); 187 | for (auto it = list.Begin(); it != list.End(); ++it) { 188 | ASSERT_NE(*it, 3); 189 | } 190 | } 191 | 192 | TEST_F(ListTest, InsertMedium) { 193 | auto it = list.Begin(); 194 | std::advance(it, list.Size() / 2); 195 | list.InsertAfter(it, 4); 196 | ASSERT_EQ(list.Size(), sz + 1); 197 | it = list.Begin(); 198 | std::advance(it, list.Size() / 2); 199 | ASSERT_EQ(*it, 4); 200 | } 201 | 202 | TEST_F(ListTest, Clear) { 203 | list.Clear(); 204 | ASSERT_TRUE(list.IsEmpty()); 205 | ASSERT_EQ(list.Size(), 0); 206 | } 207 | 208 | 209 | int main(int argc, char **argv) { 210 | ::testing::InitGoogleTest(&argc, argv); 211 | 212 | return RUN_ALL_TESTS(); 213 | } 214 | -------------------------------------------------------------------------------- /tasks/lists/list/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(list.hpp) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | add_task_test(stress_tests tests/stress.cpp) 5 | end_task() 6 | -------------------------------------------------------------------------------- /tasks/lists/list/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class ListIsEmptyException : std::exception { 7 | public: 8 | explicit ListIsEmptyException(const std::string& text) : error_message_(text) { 9 | } 10 | 11 | const char* what() const noexcept override { 12 | return error_message_.data(); 13 | } 14 | 15 | private: 16 | std::string_view error_message_; 17 | }; -------------------------------------------------------------------------------- /tasks/lists/list/images/ds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/lists/list/images/ds.png -------------------------------------------------------------------------------- /tasks/lists/list/images/image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/lists/list/images/image-1.png -------------------------------------------------------------------------------- /tasks/lists/list/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/lists/list/images/image.png -------------------------------------------------------------------------------- /tasks/lists/list/list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "exceptions.hpp" 12 | 13 | template 14 | class List { 15 | private: 16 | class Node { 17 | friend class ListIterator; 18 | friend class List; 19 | /*???*/ 20 | 21 | private: 22 | /*???*/ 23 | }; 24 | 25 | public: 26 | class ListIterator { 27 | public: 28 | // NOLINTNEXTLINE 29 | using value_type = T; 30 | // NOLINTNEXTLINE 31 | using reference_type = value_type&; 32 | // NOLINTNEXTLINE 33 | using pointer_type = value_type*; 34 | // NOLINTNEXTLINE 35 | using difference_type = std::ptrdiff_t; 36 | // NOLINTNEXTLINE 37 | using iterator_category = std::bidirectional_iterator_tag; 38 | 39 | inline bool operator==(const ListIterator&) const { 40 | std::abort(); // Not implemented 41 | }; 42 | 43 | inline bool operator!=(const ListIterator&) const { 44 | std::abort(); // Not implemented 45 | }; 46 | 47 | inline reference_type operator*() const { 48 | std::abort(); // Not implemented 49 | }; 50 | 51 | ListIterator& operator++() { 52 | std::abort(); // Not implemented 53 | }; 54 | 55 | ListIterator operator++(int) { 56 | std::abort(); // Not implemented 57 | }; 58 | 59 | ListIterator& operator--() { 60 | std::abort(); // Not implemented 61 | }; 62 | 63 | ListIterator operator--(int) { 64 | std::abort(); // Not implemented 65 | }; 66 | 67 | /*The overload of operator -> must either return a raw pointer, 68 | or return an object (by reference or by value) for which 69 | operator -> is in turn overloaded.*/ 70 | inline pointer_type operator->() const { 71 | std::abort(); // Not implemented 72 | }; 73 | 74 | private: 75 | explicit ListIterator(const Node*) { 76 | // Not implemented 77 | } 78 | 79 | private: 80 | Node* current_; 81 | }; 82 | 83 | public: 84 | List() { 85 | // Not implemented 86 | } 87 | 88 | explicit List(size_t /*sz*/) { 89 | // Not implemented 90 | } 91 | 92 | List(const std::initializer_list& /*values*/) { 93 | // Not implemented 94 | } 95 | 96 | List(const List& /*other*/) { 97 | // Not implemented 98 | } 99 | 100 | List& operator=(const List& /*other*/) { 101 | std::abort(); // Not implemented 102 | } 103 | 104 | ListIterator Begin() const noexcept { 105 | std::abort(); // Not implemented 106 | } 107 | 108 | ListIterator End() const noexcept { 109 | std::abort(); // Not implemented 110 | } 111 | 112 | inline T& Front() const { 113 | std::abort(); // Not implemented 114 | } 115 | 116 | inline T& Back() const { 117 | std::abort(); // Not implemented 118 | } 119 | 120 | inline bool IsEmpty() const noexcept { 121 | std::abort(); // Not implemented 122 | } 123 | 124 | inline size_t Size() const noexcept { 125 | std::abort(); // Not implemented 126 | } 127 | 128 | void Swap(List& /*a*/) { 129 | // Not implemented 130 | } 131 | 132 | ListIterator Find(const T& /*value*/) const { 133 | std::abort(); // Not implemented 134 | } 135 | 136 | void Erase(ListIterator /*pos*/) { 137 | // Not implemented 138 | } 139 | 140 | void Insert(ListIterator /*pos*/, const T& /*value*/) { 141 | // Not implemented 142 | } 143 | 144 | void Clear() noexcept { 145 | // Not implemented 146 | } 147 | 148 | void PushBack(const T& /*value*/) { 149 | // Not implemented 150 | } 151 | 152 | void PushFront(const T& /*value*/) { 153 | // Not implemented 154 | } 155 | 156 | void PopBack() { 157 | // Not implemented 158 | } 159 | 160 | void PopFront() { 161 | // Not implemented 162 | } 163 | 164 | ~List() { 165 | // Not implemented 166 | } 167 | 168 | private: 169 | /*???*/ 170 | }; 171 | 172 | namespace std { 173 | // Global swap overloading 174 | template 175 | // NOLINTNEXTLINE 176 | void swap(List& a, List& b) { 177 | a.Swap(b); 178 | } 179 | } // namespace std 180 | -------------------------------------------------------------------------------- /tasks/lists/list/readme.md: -------------------------------------------------------------------------------- 1 | # Двусвязный список 2 | 3 | В этой задаче напишем свой [std::list](https://en.cppreference.com/w/cpp/container/list). 4 | 5 | --- 6 | 7 | *Двусвязный список* – структура данных, представляющая собой связанные друг за другом узлы. Двусвязный означает, что связь `двунаправленная` - текущий узел связан с следующим и предыдущим.
8 | ![Alt text](./images/image-1.png) 9 | 10 | **Обладает следующими свойствами:** 11 | - Вставка элемента происходит за O(1). 12 | - Поиск элемента происходит за O(N). 13 | - Удаление элемента за O(1). 14 | 15 | ## Простые операции 16 | ```C++ 17 | // Вставить элемент в конец 18 | void PushBack(const T&); 19 | 20 | // Вставить элемент в начало 21 | void PushFront(const T&); 22 | 23 | // Удалить элемент из конца 24 | void PopBack(); 25 | 26 | // Удалить элемент из начала 27 | void PopFront(); 28 | 29 | // Поменять местами два списка 30 | void Swap(List&); 31 | 32 | // True если список пустой 33 | bool IsEmpty(); 34 | 35 | // Вернуть кол-во элементов списка 36 | size_t Size(); 37 | 38 | // Вернуть значение первого узла 39 | T& Front(); 40 | 41 | // Вернуть значение последнего узла 42 | T& Back(); 43 | ``` 44 | 45 | ## Итераторы 46 | 47 | [Итератор](https://refactoring.guru/design-patterns/iterator) - это шаблон проектирования, который позволяет перемещаться по элементам контейнера, не раскрывая его базовое представление (список, стек, дерево и т. д.). 48 | 49 | ### ☹️Проблема 50 | ![Alt text](./images/ds.png) 51 | В программировании есть большое количество структур данных. Иногда это просто массив, иногда список. А бывают и более сложные: деревья, графы, стек. 52 | 53 | Несмотря на всё разнообразие структур, нам нужно как-то единообразно получать доступ к элементам. При этом хочется получать доступ к следующему элементу, не обращаясь снова и снова к предыдущим. 54 | 55 | ### 😊Решение 56 | 57 | Основная идея шаблона «Итератор» заключается в выделении поведения обхода коллекции в отдельный объект, называемый `итератором`. 58 | 59 | Пользователь не думает о том, как устроена структура данных под ним, а имеет единый интерейс к получению данных и `итерированию` по коллекции. 60 | 61 | ### Интерфейс 62 | 63 | ```C++ 64 | template 65 | class ListIterator { 66 | public: 67 | // Сравнить на равенство два итератора 68 | bool operator==(const ListIterator&); 69 | 70 | // Сравнить на неравенство два итератора 71 | bool operator!=(const ListIterator&); 72 | 73 | // Получить значение текущей Node 74 | typename Node::value_type& operator*(); 75 | 76 | // Проитерироваться вперёд на один шаг 77 | ListIterator& operator++(); 78 | 79 | // Постфиксная версия 80 | ListIterator operator++(int); 81 | 82 | // Проитерироваться назад на один шаг 83 | ListIterator& operator--(); 84 | 85 | // Постфиксная версия 86 | ListIterator operator--(int); 87 | 88 | private: 89 | ListIterator(const Node*); 90 | /*The overload of operator -> must either return a raw pointer, 91 | or return an object (by reference or by value) for which 92 | operator -> is in turn overloaded.*/ 93 | typename Node::value_type* operator->(); 94 | private: 95 | Node* current; 96 | }; 97 | 98 | ``` 99 | 100 | ### Указания по реализации 101 | 102 | Прочтите о том, [как перегружать операторы](https://en.cppreference.com/w/cpp/language/operators) в C++. 103 | 104 | Думайте об операторе как о обычном методе класса. 105 | 106 | [Writing a custom iterator in modern C++](https://www.internalpointers.com/post/writing-custom-iterators-modern-cpp) 107 | 108 | Если список пустой, а из него что-то пытаются удалить - бросьте исключение [ListIsEmptyException](exceptions.hpp) 109 | 110 | ## Операции с итераторами в списке 111 | 112 | ![Alt text](./images/image.png) 113 | 114 | - `ListIterator Begin()` - Вернуть итератор на первый элемент. 115 | 116 | - `ListIterator End()` - Вернуть итератор на `следующий после последнего элемент`. См. картинку выше 117 | 118 | - `void Erase(ListIterator)` - Удалить элемент по переданному итератору. 119 | 120 | - `void Insert(ListIterator, const T&)` - Вставить новый элемент `до` переданного итератора. 121 | 122 | ## Задание 123 | 124 | Реализуйте [список](list.hpp) и итераторы к нему. 125 | 126 | ### Указания к реализации 127 | 128 | Чтобы лучше понять ожидаемое поведение, посмотрите [тесты](tests/unit.cpp). 129 | 130 | **В публичное API не стоит добавлять новых методов!** 131 | 132 | **В публичном API не должно быть класса `Node`!** 133 | 134 | `std::initializer_list` позволяет передавать список элементов
135 | [How to create a constructor initialized with a list?](https://stackoverflow.com/questions/21869208/how-to-create-a-constructor-initialized-with-a-list) 136 | 137 | Строки `friend class ListIterator` означают, что класс `ListIterator` имеет доступ к приватным полям и методам текущего класса, т.е. являеттся его [другом](https://en.cppreference.com/w/cpp/language/friend) 138 | 139 | ## References 140 | - [Iterator pattern](https://refactoring.guru/design-patterns/iterator) 141 | - [To Be or Not to Be (an Iterator)](https://ericniebler.com/2015/01/28/to-be-or-not-to-be-an-iterator/) 142 | 143 | ## Примечание 144 | 145 | В Стресс-тесте сравнится по скорости ваша реализация с `std::list` -------------------------------------------------------------------------------- /tasks/lists/list/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": ["list.hpp", "exceptions.hpp"], 18 | "submit_files": ["list.hpp", "exceptions.hpp"], 19 | "forbidden": [ 20 | { 21 | "patterns": [ 22 | "Not implemented" 23 | ], 24 | "hint": "You should implement this part" 25 | }, 26 | { 27 | "patterns": [ 28 | "std::list", 29 | "std::vector", 30 | "std::forward_list" 31 | ], 32 | "hint": "Don't use STL containers" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tasks/lists/list/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../list.hpp" 9 | 10 | void ConstructRandomList(List& list, int sz) { 11 | std::random_device rd; 12 | std::mt19937 mt(rd()); 13 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 14 | int random_key; 15 | while(sz) { 16 | random_key = dist(mt); 17 | list.PushBack(random_key); 18 | --sz; 19 | } 20 | } 21 | 22 | void ConstructRandomList(std::list& list, int sz) { 23 | std::random_device rd; 24 | std::mt19937 mt(rd()); 25 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 26 | int random_key; 27 | while(sz) { 28 | random_key = dist(mt); 29 | list.push_back(random_key); 30 | --sz; 31 | } 32 | } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | void BM_CustomListPushBack(benchmark::State& state) { 36 | List list; 37 | for (auto _ : state) { 38 | ConstructRandomList(list, state.range(0)); 39 | } 40 | state.SetComplexityN(state.range(0)); 41 | } 42 | 43 | void BM_StdListPushBack(benchmark::State& state) { 44 | std::list list; 45 | for (auto _ : state) { 46 | ConstructRandomList(list, state.range(0)); 47 | } 48 | state.SetComplexityN(state.range(0)); 49 | } 50 | 51 | void BM_CustomListMiddleInsert(benchmark::State& state) { 52 | List list; 53 | ConstructRandomList(list, 100); 54 | auto it = list.Begin(); 55 | std::advance(it, 50); 56 | for (auto _ : state) { 57 | for (int i = 0; i < state.range(0); ++i){ 58 | list.Insert(it, 50); 59 | } 60 | } 61 | state.SetComplexityN(state.range(0)); 62 | } 63 | 64 | void BM_StdListMiddleInsert(benchmark::State& state) { 65 | std::list list; 66 | ConstructRandomList(list, 100); 67 | auto it = list.begin(); 68 | std::advance(it, 50); 69 | for (auto _ : state) { 70 | for (int i = 0; i < state.range(0); ++i){ 71 | list.insert(it, 50); 72 | } 73 | } 74 | state.SetComplexityN(state.range(0)); 75 | } 76 | 77 | void BM_CustomListErase(benchmark::State& state) { 78 | List list; 79 | for (auto _ : state) { 80 | ConstructRandomList(list, state.range(0)); 81 | for (int64_t i = 0; i < state.range(0); ++i) { 82 | list.Erase(list.Begin()); 83 | } 84 | } 85 | state.SetComplexityN(state.range(0)); 86 | } 87 | 88 | void BM_StdListErase(benchmark::State& state) { 89 | std::list list; 90 | for (auto _ : state) { 91 | ConstructRandomList(list, state.range(0)); 92 | for (int64_t i = 0; i < state.range(0); ++i) { 93 | list.erase(list.begin()); 94 | } 95 | } 96 | state.SetComplexityN(state.range(0)); 97 | } 98 | 99 | void BM_CustomListClear(benchmark::State& state) { 100 | List list; 101 | for (auto _ : state) { 102 | ConstructRandomList(list, state.range(0)); 103 | list.Clear(); 104 | } 105 | state.SetComplexityN(state.range(0)); 106 | } 107 | 108 | void BM_StdListClear(benchmark::State& state) { 109 | std::list list; 110 | for (auto _ : state) { 111 | ConstructRandomList(list, state.range(0)); 112 | list.clear(); 113 | } 114 | state.SetComplexityN(state.range(0)); 115 | } 116 | 117 | void BM_CustomListFind(benchmark::State& state) { 118 | List list; 119 | std::random_device rd; 120 | std::mt19937 mt(rd()); 121 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 122 | int random_key; 123 | for (auto _ : state) { 124 | ConstructRandomList(list, state.range(0)); 125 | random_key = dist(mt); 126 | list.Find(random_key); 127 | } 128 | state.SetComplexityN(state.range(0)); 129 | } 130 | 131 | void BM_StdListFind(benchmark::State& state) { 132 | std::list list; 133 | std::random_device rd; 134 | std::mt19937 mt(rd()); 135 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 136 | int random_key; 137 | for (auto _ : state) { 138 | ConstructRandomList(list, state.range(0)); 139 | random_key = dist(mt); 140 | std::ignore = std::find(list.begin(), list.end(), random_key); 141 | } 142 | state.SetComplexityN(state.range(0)); 143 | } 144 | 145 | 146 | BENCHMARK(BM_CustomListPushBack)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 147 | BENCHMARK(BM_StdListPushBack)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 148 | BENCHMARK(BM_CustomListMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 149 | BENCHMARK(BM_StdListMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 150 | BENCHMARK(BM_CustomListErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 151 | BENCHMARK(BM_StdListErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 152 | BENCHMARK(BM_CustomListClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 153 | BENCHMARK(BM_StdListClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 154 | BENCHMARK(BM_CustomListFind)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 155 | BENCHMARK(BM_StdListFind)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 156 | 157 | 158 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/lists/list/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../list.hpp" 9 | 10 | class ListTest: public testing::Test { 11 | protected: 12 | void SetUp() override { 13 | list.PushBack(1); 14 | list.PushBack(2); 15 | list.PushBack(3); 16 | list.PushBack(4); 17 | list.PushBack(5); 18 | list.PushBack(6); 19 | list.PushBack(7); 20 | assert(list.Size() == sz); 21 | } 22 | List list; 23 | const size_t sz = 7; 24 | }; 25 | 26 | 27 | TEST(EmptyListTest, DefaultConstructor) { 28 | List list; 29 | ASSERT_TRUE(list.IsEmpty()) << "Default list isn't empty!"; 30 | } 31 | 32 | TEST(EmptyListTest, PushBackSimple) { 33 | List list; 34 | list.PushBack(1); 35 | ASSERT_EQ(list.Size(), 1); 36 | 37 | ASSERT_EQ(list.Front(), list.Back()); 38 | ASSERT_EQ(list.Front(), 1); 39 | } 40 | 41 | TEST(EmptyListTest, PushFrontSimple) { 42 | List list; 43 | list.PushBack(1); 44 | list.PushFront(2); 45 | ASSERT_EQ(list.Size(), 2); 46 | ASSERT_EQ(list.Front(), 2); 47 | } 48 | 49 | TEST(EmptyListTest, PopFrontSimple) { 50 | List list; 51 | list.PushBack(1); 52 | list.PushFront(2); 53 | list.PopFront(); 54 | 55 | ASSERT_EQ(list.Size(), 1); 56 | ASSERT_EQ(list.Front(), list.Back()); 57 | ASSERT_EQ(list.Front(), 1); 58 | } 59 | 60 | TEST(EmptyListTest, PopBackSimple) { 61 | List list; 62 | list.PushBack(1); 63 | list.PushFront(2); 64 | list.PopBack(); 65 | 66 | ASSERT_EQ(list.Size(), 1); 67 | ASSERT_EQ(list.Front(), list.Back()); 68 | ASSERT_EQ(list.Front(), 2); 69 | } 70 | 71 | TEST(EmptyListTest, PopBackEmptyList) { 72 | List list; 73 | EXPECT_THROW({ 74 | list.PopBack(); 75 | }, ListIsEmptyException); 76 | } 77 | 78 | TEST(EmptyListTest, PopFrontEmptyList) { 79 | List list; 80 | EXPECT_THROW({ 81 | list.PopFront(); 82 | }, ListIsEmptyException); 83 | } 84 | 85 | TEST(EmptyListTest, ConstructorSizeDefaultValues) { 86 | List list(5); 87 | ASSERT_EQ(list.Size(), 5); 88 | while (list.Size()) { 89 | ASSERT_TRUE(list.Front().empty()); 90 | list.PopFront(); 91 | } 92 | } 93 | 94 | TEST(EmptyListTest, ConstructorWithInitList) { 95 | List list{1, 2, 3, 4, 5, 6, 7, 8}; 96 | ASSERT_EQ(list.Size(), 8); 97 | int iter = 1; 98 | while (!list.IsEmpty()) { 99 | ASSERT_EQ(list.Front(), iter++); 100 | list.PopFront(); 101 | } 102 | } 103 | 104 | TEST(EmptyListTest, Swap) { 105 | List list; 106 | list.PushBack(5); 107 | 108 | List lst; 109 | lst.PushBack(15); 110 | lst.PushBack(14); 111 | 112 | size_t old_mp_size = list.Size(); 113 | size_t old_dict_size = lst.Size(); 114 | 115 | std::swap(list, lst); 116 | 117 | ASSERT_EQ(lst.Size(), old_mp_size); 118 | ASSERT_EQ(list.Size(), old_dict_size); 119 | 120 | ASSERT_EQ(lst.Front(), 5); 121 | ASSERT_EQ(list.Front(), 15); 122 | ASSERT_EQ(list.Back(), 14); 123 | } 124 | 125 | TEST(EmptyListTest, PopBackOneElement) { 126 | List lst; 127 | lst.PushBack(1); 128 | lst.PopBack(); 129 | lst.PushBack(1); 130 | lst.PopFront(); 131 | 132 | ASSERT_TRUE(lst.IsEmpty()); 133 | } 134 | 135 | TEST(EmptyListTest, PopFrontOneElement) { 136 | List lst; 137 | lst.PushBack(1); 138 | lst.PopFront(); 139 | lst.PushBack(1); 140 | lst.PopFront(); 141 | 142 | ASSERT_TRUE(lst.IsEmpty()); 143 | } 144 | 145 | TEST_F(ListTest, CopyConstructor) { 146 | List lst = list; 147 | ASSERT_NE(&list, &lst) << "Copy constructor must do copy!\n"; 148 | ASSERT_EQ(list.Size(), lst.Size()); 149 | while (!lst.IsEmpty()) { 150 | ASSERT_EQ(list.Front(), lst.Front()); 151 | list.PopFront(); 152 | lst.PopFront(); 153 | } 154 | } 155 | 156 | TEST_F(ListTest, CopyAssigment) { 157 | List lst; 158 | lst.PushBack(4); 159 | list = lst; 160 | ASSERT_NE(&list, &lst) << "Copy assigment must do copy!\n"; 161 | ASSERT_EQ(list.Size(), lst.Size()); 162 | while (!lst.IsEmpty()) { 163 | ASSERT_EQ(list.Front(), lst.Front()); 164 | list.PopFront(); 165 | lst.PopFront(); 166 | } 167 | } 168 | 169 | TEST_F(ListTest, SelfAssignment) { 170 | std::thread thread([&](){ 171 | list = list; 172 | }); 173 | auto future = std::async(std::launch::async, &std::thread::join, &thread); 174 | ASSERT_LT( 175 | future.wait_for(std::chrono::seconds(1)), 176 | std::future_status::timeout 177 | ) << "There is infinity loop!\n"; 178 | } 179 | 180 | TEST_F(ListTest, RangeWithIteratorPreFix) { 181 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 182 | "Distanse between begin and end iterators ins't equal size"; 183 | int iter = 1; 184 | for (auto it = list.Begin(); it != list.End(); ++it) { 185 | ASSERT_EQ(*it, iter++); 186 | } 187 | } 188 | 189 | TEST_F(ListTest, RangeWithIteratorPostFix) { 190 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 191 | "Distanse between begin and end iterators ins't equal size"; 192 | int iter = 1; 193 | for (auto it = list.Begin(); it != list.End(); it++) { 194 | ASSERT_EQ(*it, iter++); 195 | } 196 | } 197 | 198 | TEST_F(ListTest, ReverseRangeWithIteratorPreFix) { 199 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 200 | "Distanse between begin and end iterators ins't equal size"; 201 | int iter = sz; 202 | auto it = list.End(); 203 | for (size_t i = sz; i > 0; --i) { 204 | --it; 205 | ASSERT_EQ(*it, iter--); 206 | } 207 | } 208 | 209 | TEST_F(ListTest, ReverseRangeWithIteratorPostFix) { 210 | ASSERT_EQ(std::distance(list.Begin(), list.End()), sz) << 211 | "Distanse between begin and end iterators ins't equal size"; 212 | auto it = list.End(); 213 | int iter = sz; 214 | for (size_t i = sz; i > 0; --i) { 215 | it--; 216 | ASSERT_EQ(*it, iter--); 217 | } 218 | } 219 | 220 | TEST_F(ListTest, FindFirst) { 221 | auto it = list.Begin(); 222 | ASSERT_EQ(it, list.Find(1)); 223 | } 224 | 225 | TEST_F(ListTest, FindLast) { 226 | auto it = list.End(); 227 | it--; 228 | ASSERT_EQ(it, list.Find(7)); 229 | } 230 | 231 | TEST_F(ListTest, FindFail) { 232 | auto it = list.End(); 233 | ASSERT_EQ(it, list.Find(999)); 234 | } 235 | 236 | TEST_F(ListTest, FindAll) { 237 | auto it = list.Begin(); 238 | for (int i = 1; i < 8; ++i) { 239 | ASSERT_EQ(it++, list.Find(i)); 240 | } 241 | } 242 | 243 | TEST_F(ListTest, EraseBegin) { 244 | int begin_value = list.Front(); 245 | list.Erase(list.Begin()); 246 | ASSERT_EQ(list.Size(), sz - 1); 247 | ASSERT_NE(list.Front(), begin_value); 248 | } 249 | 250 | TEST_F(ListTest, EraseMedium) { 251 | auto it = list.Begin(); 252 | std::advance(it, list.Size() / 2); 253 | list.Erase(it); 254 | ASSERT_EQ(list.Size(), sz - 1); 255 | for (auto it = list.Begin(); it != list.End(); ++it) { 256 | ASSERT_NE(*it, 4); 257 | } 258 | } 259 | 260 | TEST_F(ListTest, InsertMedium) { 261 | auto it = list.Begin(); 262 | std::advance(it, list.Size() / 2); 263 | list.Insert(it, 4); 264 | ASSERT_EQ(list.Size(), sz + 1); 265 | it = list.Begin(); 266 | std::advance(it, list.Size() / 2); 267 | ASSERT_EQ(*it, 4); 268 | --it; 269 | ASSERT_EQ(*it, 4); 270 | } 271 | 272 | TEST_F(ListTest, Clear) { 273 | list.Clear(); 274 | ASSERT_TRUE(list.IsEmpty()); 275 | ASSERT_EQ(list.Size(), 0); 276 | } 277 | 278 | 279 | int main(int argc, char **argv) { 280 | ::testing::InitGoogleTest(&argc, argv); 281 | 282 | return RUN_ALL_TESTS(); 283 | } -------------------------------------------------------------------------------- /tasks/lists/readme.md: -------------------------------------------------------------------------------- 1 | # Линейные списки 2 | 3 | - [Односвязный список](forward) 4 | - [Двусвязный список](list) 5 | -------------------------------------------------------------------------------- /tasks/tree/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | add_subdirectory(bst) 4 | add_subdirectory(NTree) 5 | add_subdirectory(iterators) -------------------------------------------------------------------------------- /tasks/tree/NTree/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | add_task_library(filesystem) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | end_task() 5 | -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/detail/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace filesystem::exceptions { 9 | 10 | class FileNotFoundException : std::exception { 11 | public: 12 | explicit FileNotFoundException(const std::string& filename) : error_message_(filename) { 13 | } 14 | 15 | const char* what() const noexcept override { 16 | return error_message_.data(); 17 | } 18 | 19 | private: 20 | std::string_view error_message_; 21 | }; 22 | 23 | } // end namespace filesystem::exceptions -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/files/directory.cpp: -------------------------------------------------------------------------------- 1 | #include "directory.hpp" 2 | 3 | namespace filesystem {} // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/files/directory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../fs.hpp" 6 | #include "../map/map.hpp" 7 | #include "file.hpp" 8 | 9 | namespace filesystem { 10 | 11 | class Directory { 12 | friend class Fs; 13 | 14 | void GetName() const; 15 | 16 | private: 17 | Directory* parent; 18 | Map childs; 19 | Map files; 20 | }; 21 | 22 | } // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/files/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.hpp" 2 | 3 | namespace filesystem { 4 | 5 | void File::Read(size_t /*bytes*/) const { 6 | std::abort(); // Not implemented 7 | } 8 | 9 | void File::Write() { 10 | // Not implemented 11 | } 12 | 13 | void File::Append() { 14 | // Not implemented 15 | } 16 | 17 | } // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/files/file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace filesystem { 6 | 7 | class File { 8 | public: 9 | void Read(size_t /*bytes*/) const; 10 | 11 | // overwrite 12 | void Write(); 13 | 14 | void Append(); 15 | 16 | private: 17 | std::string content; 18 | }; 19 | 20 | } // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/fs.cpp: -------------------------------------------------------------------------------- 1 | #include "fs.hpp" 2 | 3 | namespace filesystem { 4 | 5 | void Fs::ChangeDir(const std::string& /*path*/) { 6 | // Not implemented 7 | } 8 | 9 | void Fs::ListFiles(const std::string& /*path = "."*/) const { 10 | // Not implemented 11 | } 12 | 13 | void Fs::MakeDir(const std::string& /*path*/, bool /*is_create_parents = false*/ 14 | ) { 15 | // Not implemented 16 | } 17 | 18 | auto Fs::Split(const std::string& /*str*/, const std::string& /*splitter*/) -> std::vector { 19 | std::abort(); // Not Implemented 20 | } 21 | 22 | void Fs::RemoveFile(const std::string& /*path*/) { 23 | // Not implemented 24 | } 25 | 26 | void Fs::CreateFile(const std::string& /*path*/, bool /*is_overwrite = false*/ 27 | ) { 28 | // Not implemented 29 | } 30 | 31 | void Fs::WriteToFile(const std::string& /*path*/, bool /*is_overwrite = false*/, std::ostringstream& /*stream*/ 32 | ) { 33 | // Not implemented 34 | } 35 | 36 | void Fs::ShowFileContent(const std::string& /*path*/) { 37 | // Not implemented 38 | } 39 | 40 | void Fs::FindFile(const std::string& /*filename*/) { 41 | // Not implemented 42 | } 43 | 44 | void Fs::PWD() const { 45 | // Not implemented 46 | } 47 | 48 | } // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/fs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "./detail/exceptions.hpp" 6 | #include "./files/directory.hpp" 7 | 8 | namespace filesystem { 9 | 10 | class Fs { 11 | public: 12 | void ChangeDir(const std::string& /*path*/); 13 | 14 | void PWD() const; 15 | 16 | void RemoveFile(const std::string& /*path*/); 17 | 18 | void ListFiles(const std::string& /*path = "."*/) const; 19 | 20 | void MakeDir(const std::string& /*path*/, bool /*is_create_parents = false*/); 21 | 22 | void CreateFile(const std::string& /*path = "."*/, bool /*is_overwrite = false*/); 23 | 24 | void WriteToFile(const std::string& /*filename*/, bool /*is_overwrite = false*/, std::ostringstream& /*stream*/ 25 | ); 26 | 27 | void ShowFileContent(const std::string& /*path*/); 28 | 29 | void FindFile(const std::string& /*filename*/); 30 | 31 | private: 32 | std::vector Split(const std::string& /*str*/, const std::string& /*splitter*/); 33 | 34 | private: 35 | /* 36 | filesystem::files::Directory* root; 37 | filesystem::files::Directory* current; 38 | */ 39 | }; 40 | 41 | } // end namespace filesystem -------------------------------------------------------------------------------- /tasks/tree/NTree/filesystem/map/map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template > 9 | class Map { 10 | public: 11 | Map() { 12 | // Not implemented 13 | } 14 | 15 | Value& operator[](const Key& /*key*/) { 16 | std::abort(); // Not implemented 17 | } 18 | 19 | inline bool IsEmpty() const noexcept { 20 | std::abort(); // Not implemented 21 | } 22 | 23 | inline size_t Size() const noexcept { 24 | std::abort(); // Not implemented 25 | } 26 | 27 | void Swap(Map& a) { 28 | static_assert(std::is_samecomp), decltype(a.comp)>::value, 29 | "The compare function types are different"); 30 | // Not implemented 31 | } 32 | 33 | std::vector> Values(bool /*is_increase=true*/) const noexcept { 34 | std::abort(); // Not implemented 35 | } 36 | 37 | void Insert(const std::pair& /*val*/) { 38 | // Not implemented 39 | } 40 | 41 | void Insert(const std::initializer_list>& /*values*/) { 42 | // Not implemented 43 | } 44 | 45 | void Erase(const Key& /*key*/) { 46 | // Not implemented 47 | } 48 | 49 | void Clear() noexcept { 50 | // Not implemented 51 | } 52 | 53 | bool Find(const Key& /*key*/) const { 54 | std::abort(); // Not implemented 55 | } 56 | 57 | ~Map() { 58 | // Not implemented 59 | } 60 | 61 | private: 62 | class Node { 63 | friend class Map; 64 | 65 | private: 66 | /*???*/ 67 | }; 68 | /*???*/ 69 | 70 | private: 71 | Compare comp; 72 | /*???*/ 73 | }; 74 | 75 | namespace std { 76 | // Global swap overloading 77 | template 78 | void swap(Map& a, Map& b) { 79 | a.Swap(b); 80 | } 81 | } // namespace std -------------------------------------------------------------------------------- /tasks/tree/NTree/readme.md: -------------------------------------------------------------------------------- 1 | # N-ary Tree 2 | 3 | ## Пререквизиты 4 | 5 | - [tree/bst](/tasks/tree/bst) - рекомендуется 6 | - [vector](/tasks/vector) - рекомендуется 7 | 8 | --- 9 | 10 | В этой задаче напишем свою простую файловую систему, представленную в виде N-ary tree - дерева с переменным количеством детей. 11 | 12 | ## Публичный API 13 | 14 | Мы реализуем простые операции 15 | 16 | - `ChangeDir(path)` - изменить текущую рабочую директорию 17 | - `PWD()` - напечатать текущую рабочую директорию 18 | - `ListFiles(path)` - напечатать список файлов в отстортированном лексикографическом порядке 19 | - `MakeDir(path, is_create_parents)` - Создать папку. Второй флаг позволяет создать все новые папки на пути 20 | - `CreateFile(path, is_overwrite)` - создать файл. Если уже существует, второй флаг позволит перезаписать. 21 | - `WriteToFile(path, is_overwrite, stream)` - написать в файл. Если флаг = true, то перезаписаписываем, если false - добавляем в конец. WriteToFile должен считывать данные из переданного потока. 22 | - `RemoveFile(path)` - удалить файл. Если в пути указана папка, то каскадно удалить всё поддерево этой папки. 23 | - `ShowFileContent(path)` - распечатать содержимое файла 24 | - `FindFile(filename)` - найти файл по имени. Выводим полный путь до файла. Если не найден - пишем об этом сообщение. 25 | 26 | Во всех операциях присутствует поиск файла. Если файл не найден, бросьте исключение [`FileNotFoundException`](filesystem/detail/exceptions.hpp). 27 | 28 | ## Компоненты 29 | 30 | Файловая система состоит из узлов - [папок](filesystem/files/directory.hpp). Каждый узел хранит [файлы](filesystem/files/file.hpp), указатель на родителя и список своих детей - вложенных папок. 31 | 32 | ## Пути 33 | 34 | Путь в Linux выглядит так: 35 | ```/mnt/c/Users/Me/file.hpp``` 36 | 37 | В данном случае это абсолютный путь - от корня до текущего файла/папки. 38 | 39 | Бывают также относительные пути: 40 | ```../../../filesytem/``` 41 | 42 | `..` - означают вверх по дереву. Для простой поддержки относительных путей, узел должен хранить указатель на родителя. 43 | 44 | ### Split 45 | В пути есть чёткие указания в какой узел идти - название папки указано между символами косой черты. 46 | 47 | Здесь пригодился бы split из Python. Отлично, реализуйте его! 48 | 49 | *Важно отметить, что пути могут быть разными. Например, в Windows приняты двойные обратные слэши. Поэтому в общем случае `splitter` - это строка, а не символ `/`.* 50 | 51 | ## Задание 52 | 53 | Реализуйте простую файловую систему построенную на `N-ary Tree`. 54 | 55 | ### Указания к реализации 56 | 57 | Для хранения файлов и детей можно использовать vector. Однако, нам необходимо быстро искать и получать файлы в отсортированном виде. Тут пригодился бы `Map`, реализованный вами в задаче `bst`. 58 | 59 | Используйте его. В качестве ключа используйте имена файлов/папок. 60 | 61 | Классы `Directory` и `File` не должны быть доступны пользователю напрямую. Пользователь работает только с файловой системой, про её устройство он знать не должен. 62 | 63 | Сделайте все данные и методы приватными в `Directory` и `File`, добавив доступ только классу `Fs`. 64 | 65 | -------------------------------------------------------------------------------- /tasks/tree/NTree/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | } 10 | ], 11 | "lint_files": ["filesystem"], 12 | "submit_files": ["filesystem"], 13 | "forbidden": [ 14 | { 15 | "patterns": [ 16 | "std::map" 17 | ], 18 | "hint": "Don't use std::map -> use custom" 19 | }, 20 | { 21 | "patterns": [ 22 | "std::sort" 23 | ], 24 | "hint": "Don't use std::sort" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tasks/tree/NTree/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "../filesystem/fs.hpp" 12 | 13 | class FsTest: public testing::Test { 14 | protected: 15 | void SetUp() override { 16 | fs.MakeDir("/mnt/c/Users/Sysoev/VSCodeProjects/", true); 17 | fs.CreateFile("/mnt/c/Users/Sysoev/VSCodeProjects/test.txt", false); 18 | fs.CreateFile("/mnt/c/Users/Sysoev/VSCodeProjects/test1.txt", false); 19 | fs.CreateFile("/mnt/c/Users/Sysoev/VSCodeProjects/test2.txt", false); 20 | fs.CreateFile("/mnt/c/Users/Sysoev/VSCodeProjects/test3.txt", false); 21 | fs.CreateFile("/mnt/c/Users/Sysoev/test3.txt", false); 22 | fs.CreateFile("/mnt/c/Users/test1.txt", false); 23 | } 24 | 25 | public: 26 | filesystem::Fs fs; 27 | }; 28 | 29 | 30 | TEST(EmptyFsTest, PWDOnRoot) { 31 | filesystem::Fs fs; 32 | fs.PWD(); 33 | // expect: "/" 34 | } 35 | 36 | TEST(EmptyFsTest, EmptyListFiles) { 37 | filesystem::Fs fs; 38 | fs.ListFiles("."); 39 | // expect: 40 | } 41 | 42 | TEST(EmptyFsTest, SimpleMakeDir) { 43 | filesystem::Fs fs; 44 | fs.MakeDir("/dir", false); 45 | fs.ChangeDir("/dir"); 46 | fs.PWD(); 47 | // expect: "/dir" 48 | } 49 | 50 | TEST(EmptyFsTest, MakeDirWithFlag) { 51 | filesystem::Fs fs; 52 | fs.MakeDir("/dir/other_dir", true); 53 | fs.ChangeDir("/dir"); 54 | fs.ChangeDir("other_dir"); 55 | fs.PWD(); 56 | // expect: "/dir/other_dir" 57 | } 58 | 59 | TEST(EmptyFsTest, MakeSeveralDirsWithoutFlag) { 60 | filesystem::Fs fs; 61 | EXPECT_THROW({ 62 | fs.MakeDir("/dir/other_dir", false); 63 | }, filesystem::exceptions::FileNotFoundException); 64 | } 65 | 66 | TEST(EmptyFsTest, ChangeDirToNotExistingDir) { 67 | filesystem::Fs fs; 68 | EXPECT_THROW({ 69 | fs.ChangeDir("/dir"); 70 | }, filesystem::exceptions::FileNotFoundException); 71 | } 72 | 73 | TEST(EmptyFsTest, SimpleCreateFile) { 74 | filesystem::Fs fs; 75 | fs.CreateFile("file.txt", false); 76 | fs.ListFiles("."); 77 | // expect: "file.txt" 78 | } 79 | 80 | TEST(EmptyFsTest, ShowFileContent) { 81 | filesystem::Fs fs; 82 | std::ostringstream oss; 83 | oss << "Hello world!\n"; 84 | fs.CreateFile("file.txt", false); 85 | fs.WriteToFile("file.txt", false, oss); 86 | fs.ShowFileContent("file.txt"); 87 | // expect: "Hello world!" 88 | } 89 | 90 | TEST(EmptyFsTest, OverwriteFile) { 91 | filesystem::Fs fs; 92 | std::ostringstream oss; 93 | oss << "Hello world!\n"; 94 | fs.CreateFile("file.txt", false); 95 | fs.WriteToFile("file.txt", false, oss); 96 | oss << "Hello Meeeen!\n"; 97 | fs.WriteToFile("file.txt", true, oss); 98 | fs.ShowFileContent("file.txt"); 99 | // expect: "Hello Meeeen!" 100 | } 101 | 102 | TEST(EmptyFsTest, OverwriteFileByCreate) { 103 | filesystem::Fs fs; 104 | std::ostringstream oss; 105 | oss << "Hello world!\n"; 106 | fs.CreateFile("file.txt", false); 107 | fs.WriteToFile("file.txt", false, oss); 108 | fs.CreateFile("file.txt", true); 109 | fs.ShowFileContent("file.txt"); 110 | // expect: 111 | } 112 | 113 | TEST(EmptyFsTest, OverwriteFirstCreateFile) { 114 | filesystem::Fs fs; 115 | std::ostringstream oss; 116 | oss << "Hello world!\n"; 117 | fs.CreateFile("file.txt", true); 118 | fs.WriteToFile("file.txt", false, oss); 119 | fs.ShowFileContent("file.txt"); 120 | // expect: 121 | } 122 | 123 | TEST(EmptyFsTest, SimpleRemoveFile) { 124 | filesystem::Fs fs; 125 | std::ostringstream oss; 126 | oss << "Hello world!\n"; 127 | fs.CreateFile("file.txt", false); 128 | fs.RemoveFile("file.txt"); 129 | fs.ListFiles("."); 130 | // expect: 131 | } 132 | 133 | TEST(EmptyFsTest, RemoveNotExistFile) { 134 | filesystem::Fs fs; 135 | EXPECT_THROW({ 136 | fs.RemoveFile("file.txt"); 137 | }, filesystem::exceptions::FileNotFoundException); 138 | } 139 | 140 | 141 | TEST_F(FsTest, SimpleFind) { 142 | fs.FindFile("test3.txt"); 143 | // expect: 144 | // /mnt/c/Users/Sysoev/VSCodeProjects/test3.txt 145 | // /mnt/c/Users/Sysoev/test3.txt 146 | // 147 | } 148 | 149 | TEST_F(FsTest, FailedChangeDirToFile) { 150 | EXPECT_THROW({ 151 | fs.ChangeDir("/mnt/c/Users/Sysoev/test3.txt"); 152 | }, filesystem::exceptions::FileNotFoundException); 153 | } 154 | 155 | TEST_F(FsTest, DirNotExistForCreateFile) { 156 | EXPECT_THROW({ 157 | fs.ChangeDir("/mnt/c/Users/Sysoev/VSCodeProjects"); 158 | fs.PWD(); 159 | // expect: /mnt/c/Users/Sysoev/VSCodeProjects/ 160 | fs.CreateFile("/dir/file.txt", false); 161 | }, filesystem::exceptions::FileNotFoundException); 162 | } 163 | 164 | TEST_F(FsTest, FileNotFound) { 165 | EXPECT_THROW({ 166 | fs.FindFile("file.cpp"); 167 | }, filesystem::exceptions::FileNotFoundException); 168 | } 169 | 170 | TEST_F(FsTest, DeleteRoot) { 171 | fs.RemoveFile("/"); 172 | fs.ListFiles("."); 173 | // expect: 174 | } 175 | 176 | int main(int argc, char **argv) { 177 | ::testing::InitGoogleTest(&argc, argv); 178 | 179 | return RUN_ALL_TESTS(); 180 | } -------------------------------------------------------------------------------- /tasks/tree/bst/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(map.hpp) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | add_task_test(stress_tests tests/stress.cpp) 5 | end_task() 6 | -------------------------------------------------------------------------------- /tasks/tree/bst/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maxsmile123/Algorithms-And-DataStructure-Course/7c9c8b779eef43826a2d763284bd2d3de8554e16/tasks/tree/bst/images/image.png -------------------------------------------------------------------------------- /tasks/tree/bst/map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template > 11 | class Map { 12 | public: 13 | Map() { 14 | // Not implemented 15 | } 16 | 17 | Value& operator[](const Key& /*key*/) { 18 | std::abort(); // Not implemented 19 | } 20 | 21 | inline bool IsEmpty() const noexcept { 22 | std::abort(); // Not implemented 23 | } 24 | 25 | inline size_t Size() const noexcept { 26 | std::abort(); // Not implemented 27 | } 28 | 29 | void Swap(Map& a) { 30 | static_assert(std::is_samecomp), decltype(a.comp)>::value, 31 | "The compare function types are different"); 32 | // Not implemented 33 | } 34 | 35 | std::vector> Values(bool /*is_increase=true*/) const noexcept { 36 | std::abort(); // Not implemented 37 | } 38 | 39 | void Insert(const std::pair& /*val*/) { 40 | // Not implemented 41 | } 42 | 43 | void Insert(const std::initializer_list>& /*values*/) { 44 | // Not implemented 45 | } 46 | 47 | void Erase(const Key& /*key*/) { 48 | // Not implemented 49 | } 50 | 51 | void Clear() noexcept { 52 | // Not implemented 53 | } 54 | 55 | bool Find(const Key& /*key*/) const { 56 | std::abort(); // Not implemented 57 | } 58 | 59 | ~Map() { 60 | // Not implemented 61 | } 62 | 63 | private: 64 | class Node { 65 | friend class Map; 66 | 67 | private: 68 | /*???*/ 69 | }; 70 | /*???*/ 71 | 72 | private: 73 | Compare comp; 74 | /*???*/ 75 | }; 76 | 77 | namespace std { 78 | // Global swap overloading 79 | template 80 | void swap(Map& a, Map& b) { 81 | a.Swap(b); 82 | } 83 | } // namespace std -------------------------------------------------------------------------------- /tasks/tree/bst/readme.md: -------------------------------------------------------------------------------- 1 | # Бинарное дерево поиска 2 | 3 | ## Пререквизиты 4 | 5 | - [lists/list](/tasks/lists/list) 6 | 7 | --- 8 | 9 | В этой задаче напишем свой [словарь](https://docs.python.org/3/tutorial/datastructures.html#dictionaries), более известный как `map` 10 | 11 | --- 12 | 13 | *BST* – структура данных, которая выполняет операции поиска, вставки, удаления **в среднем** за O(log n). 14 | 15 | **Обладает следующими свойствами:** 16 | - Максимум 2 ребёнка (бинарное дерево). 17 | - Ключ левого ребёнока меньше текущего. 18 | - Ключ правого ребёнока больше текущего. 19 | 20 | За счёт двух последних свойств может применяться [бинарный поиск](https://agorinenko.github.io/data-structures-and-algorithms/tutorial/binary_search.html). Слева элементы всегда меньше, справа - больше. 21 | 22 | У каждого узла есть ключ (`Key`) по которому происходит поиск и значение (`Value`), которое хранится в структуре. 23 | 24 | Основные операции: 25 | - Вставка элемента: `void Insert(const std::pair&)` 26 | - Удаление элемента: `void Erase(const Key&)` 27 | - Поиск элемента: `bool Find(const Key&)` 28 | 29 | ### `Insert` 30 | 31 | На вход принимает `std::pair` 32 | 33 | См. [std::pair](https://en.cppreference.com/w/cpp/utility/pair) 34 | 35 | Обратите внимание, что первый элемент в pair `const Key`, а не просто `Key`! 36 | 37 | Алгоритм можно разбить на три шага: 38 | 1) Найти позицию для вставки бинарным поиском 39 | 2) Создать новый узел с вставляемыми данными 40 | 3) Связать новый узел с узлом из пункта 1 41 | 42 | Если данный ключ уже есть в дереве - перезаписываем данные. 43 | 44 | ### `Erase` 45 | На вход принимает ключ, по которому нужно найти узел для удаления. 46 | 47 | Алгоритм можно разбить на три шага: 48 | 1) Найти родителя удаляемого узла
49 | `a)` **У удаляемого узла нет детей (он лист)**
50 | - Удаляем, указатель у родителя переводим в nullptr
51 | `b)` **У удаляемого узла есть только левый сын**
52 | - Связываем родителя с левым сыном
53 | `c)` **У удаляемого узла есть только правый сын**
54 | - Связываем родителя с правым сыном
55 | `d)` **У удаляемого узла есть оба ребёнка**
56 | - На место удаляемого узла помещаем минимальный элемент в данном поддереве
57 | 3) Удалить указанный узел 58 | 59 | Если узел не найден, бросьте исключение `std::runtime_error`: 60 | ```C++ 61 | throw std::runtime_error("Value not found"); 62 | ``` 63 | 64 | 65 | ### `Find` 66 | На вход принимает ключ, по которому нужно найти узел. 67 | 68 | Возвращаем bool: `true`, если значение есть, `false` - если нет. 69 | 70 | Алгоритм можно описать так: 71 | 1) Если текущий корень == `nullptr` - возвращаем `false`. 72 | 2) Если искомый ключ меньше текущего - идём влево. 73 | 3) Если искомый ключ больше текущего - идём вправо. 74 | 4) Если искомый ключ равен текущему - возвращаем `true`. 75 | 76 | ## Словарь в `std` 77 | 78 | См. [std::map](https://en.cppreference.com/w/cpp/container/map) 79 | 80 | ### Red-Black Tree 81 | 82 | Стоит отметить, что сложность операций будет O(log n) **в среднем**. Однако, если вставлять отсортированные элементы, дерево может выстраиваться в `лесенку`:

83 | ![Alt text](images/image.png) 84 | 85 | В таком случае все операции станут выполняться за `O(N)`. 86 | 87 | Чтобы этого избежать существуют `сбалансированные деревья поиска` - дерево, в котором высоты любого левого и правого поддерева отличаются не более чем на 1. 88 | 89 | Путём дополнительных операций `балансировки`, достигается `O(log n)` во всех случаях. 90 | 91 | Именно эти деревья реализованы в `std::map` и `std::set`. 92 | 93 | См. [std::set](https://en.cppreference.com/w/cpp/container/set) 94 | 95 | ## Задание 96 | 97 | Реализуйте [словарь](map.hpp) с помощью бинарного дерева поиска. 98 | 99 | ### Указания к реализации 100 | 101 | Во всех операциях используется поиск. Подумайте над тем, как можно избавиться от дублирования кода в этих местах. 102 | 103 | `Запрещено хранить указатель на родителя в Node!` 104 | 105 | **В публичное API не стоит добавлять новых методов!** 106 | 107 | **В публичном API не должно быть класса `Node`!** 108 | 109 | `Compare` - это функция, которая возвращает true, если первый элемент меньше второго. В `std::map` пользователь может задать свою собственную функцию сравнения объектов. Вместо операторов `<` или `>` используйте функцию `Compare(val1, val2)`. 110 | 111 | `std::initializer_list` позволяет передавать список элементов.
112 | [How to create a constructor initialized with a list?](https://stackoverflow.com/questions/21869208/how-to-create-a-constructor-initialized-with-a-list) 113 | 114 | `Values` возвращает пользователю [`std::vector`](https://en.cppreference.com/w/cpp/container/vector) пар ключ-значение. Принимает булевский параметр `is_increase`. Если он `true` - данные в векторе должны быть упорядочены по возрастанию, если `false` - по убыванию. 115 | 116 | 117 | ## References 118 | - [std::less](https://en.cppreference.com/w/cpp/utility/functional/less) 119 | - [Why `std::pair` is smelly](https://arne-mertz.de/2017/03/smelly-pair-tuple/) 120 | - [Red-Black Tree](https://algorithmtutor.com/Data-Structures/Tree/Red-Black-Trees/) 121 | 122 | ## Примечание 123 | 124 | В деревьях из `std` применяются итераторы. У вас будет возможность реализовать их позже в задаче [iterators](../iterators) -------------------------------------------------------------------------------- /tasks/tree/bst/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": ["map.hpp"], 18 | "submit_files": ["map.hpp"], 19 | "forbidden": [ 20 | { 21 | "patterns": [ 22 | "Not implemented" 23 | ], 24 | "hint": "You should implement this part" 25 | }, 26 | { 27 | "patterns": [ 28 | "std::map" 29 | ], 30 | "hint": "Don't use std::map -> implement him" 31 | }, 32 | { 33 | "patterns": [ 34 | "std::sort" 35 | ], 36 | "hint": "Don't use std::sort -> BST doesn't need this" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tasks/tree/bst/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../map.hpp" 9 | 10 | void ConstructRandomMap(Map& mp, int sz) { 11 | std::random_device rd; 12 | std::mt19937 mt(rd()); 13 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 14 | int random_key; 15 | while(sz) { 16 | random_key = dist(mt); 17 | mp.Insert(std::pair{random_key, 1}); 18 | --sz; 19 | } 20 | } 21 | 22 | void ConstructRandomMap(std::map& mp, int sz) { 23 | std::random_device rd; 24 | std::mt19937 mt(rd()); 25 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 26 | int random_key; 27 | while(sz) { 28 | random_key = dist(mt); 29 | mp.insert(std::pair{random_key, 1}); 30 | --sz; 31 | } 32 | } 33 | 34 | void ConstructLinearMap(Map& mp, int sz) { 35 | while(sz) { 36 | mp.Insert(std::pair{sz, 1}); 37 | --sz; 38 | } 39 | } 40 | 41 | void ConstructLinearMap(std::map& mp, int sz) { 42 | while(sz) { 43 | mp.insert(std::pair{sz, 1}); 44 | --sz; 45 | } 46 | } 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | void BM_CustomMapRandomInsert(benchmark::State& state) { 50 | Map mp; 51 | for (auto _ : state) { 52 | ConstructRandomMap(mp, state.range(0)); 53 | } 54 | state.SetComplexityN(state.range(0)); 55 | } 56 | 57 | void BM_StdMapRandomInsert(benchmark::State& state) { 58 | std::map mp; 59 | for (auto _ : state) { 60 | ConstructRandomMap(mp, state.range(0)); 61 | } 62 | state.SetComplexityN(state.range(0)); 63 | } 64 | 65 | void BM_CustomMapLinearInsert(benchmark::State& state) { 66 | Map mp; 67 | for (auto _ : state) { 68 | ConstructLinearMap(mp, state.range(0)); 69 | } 70 | state.SetComplexityN(state.range(0)); 71 | } 72 | 73 | void BM_StdMapLinearInsert(benchmark::State& state) { 74 | std::map mp; 75 | for (auto _ : state) { 76 | ConstructLinearMap(mp, state.range(0)); 77 | } 78 | state.SetComplexityN(state.range(0)); 79 | } 80 | 81 | void BM_CustomMapErase(benchmark::State& state) { 82 | Map mp; 83 | std::random_device rd; 84 | std::mt19937 mt(rd()); 85 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 86 | int random_key; 87 | for (auto _ : state) { 88 | ConstructRandomMap(mp, state.range(0)); 89 | for (int64_t i = 0; i < state.range(0); ++i) { 90 | random_key = dist(mt); 91 | try{ 92 | mp.Erase(random_key); 93 | } catch(...){} 94 | } 95 | } 96 | state.SetComplexityN(state.range(0)); 97 | } 98 | 99 | void BM_StdMapErase(benchmark::State& state) { 100 | std::map mp; 101 | std::random_device rd; 102 | std::mt19937 mt(rd()); 103 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 104 | int random_key; 105 | for (auto _ : state) { 106 | ConstructRandomMap(mp, state.range(0)); 107 | for (int64_t i = 0; i < state.range(0); ++i) { 108 | random_key = dist(mt); 109 | try{ 110 | mp.erase(random_key); 111 | } catch(...){} 112 | } 113 | } 114 | state.SetComplexityN(state.range(0)); 115 | } 116 | 117 | void BM_CustomMapClear(benchmark::State& state) { 118 | Map mp; 119 | for (auto _ : state) { 120 | ConstructRandomMap(mp, state.range(0)); 121 | mp.Clear(); 122 | } 123 | state.SetComplexityN(state.range(0)); 124 | } 125 | 126 | void BM_StdMapClear(benchmark::State& state) { 127 | std::map mp; 128 | for (auto _ : state) { 129 | ConstructRandomMap(mp, state.range(0)); 130 | mp.clear(); 131 | } 132 | state.SetComplexityN(state.range(0)); 133 | } 134 | 135 | 136 | BENCHMARK(BM_CustomMapRandomInsert)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 137 | BENCHMARK(BM_StdMapRandomInsert)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 138 | BENCHMARK(BM_CustomMapLinearInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 139 | BENCHMARK(BM_StdMapLinearInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 140 | BENCHMARK(BM_CustomMapErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 141 | BENCHMARK(BM_StdMapErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 142 | BENCHMARK(BM_CustomMapClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 143 | BENCHMARK(BM_StdMapClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 144 | 145 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/tree/bst/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "../map.hpp" 12 | 13 | class MapTest: public testing::Test { 14 | protected: 15 | void SetUp() override { 16 | mp.Insert({ 17 | {1, 5}, 18 | {3, 10}, 19 | {5, 90}, 20 | {10, -10}, 21 | {90, 0}, 22 | {-10, 5}, 23 | {0, 4} 24 | }); 25 | assert(mp.Size() == sz); 26 | } 27 | 28 | Map mp; 29 | const size_t sz = 7; 30 | }; 31 | 32 | 33 | TEST(EmptyMapTest, DefaultConstructor) { 34 | Map map; 35 | ASSERT_TRUE(map.IsEmpty()) << "Default Map isn't empty!"; 36 | } 37 | 38 | TEST(EmptyMapTest, InsertRoot) { 39 | Map map; 40 | map.Insert({1, 5}); 41 | ASSERT_EQ(map.Size(), 1); 42 | 43 | auto vals = map.Values(true); 44 | 45 | ASSERT_EQ(vals.size(), 1); 46 | ASSERT_EQ(vals[0].first, 1); 47 | ASSERT_EQ(vals[0].second, 5); 48 | } 49 | 50 | TEST(EmptyMapTest, InsertRootLeftRight) { 51 | Map map; 52 | map.Insert({1, 1}); 53 | map.Insert({3, 2}); 54 | map.Insert({0, 0}); 55 | 56 | ASSERT_EQ(map.Size(), 3); 57 | 58 | auto vals = map.Values(true); 59 | ASSERT_EQ(vals.size(), 3); 60 | 61 | for (size_t i = 0; i < vals.size(); ++i) { 62 | ASSERT_EQ(vals[i].second, i) << 63 | fmt::format("Values isn't equal on {} index", i); 64 | } 65 | } 66 | 67 | TEST(EmptyMapTest, InsertIncreaseSeq) { 68 | Map map; 69 | map.Insert({ 70 | {1, 0}, 71 | {3, 1}, 72 | {5, 2}, 73 | {10, 3}, 74 | {90, 4} 75 | }); 76 | ASSERT_EQ(map.Size(), 5); 77 | 78 | auto vals = map.Values(true); 79 | ASSERT_EQ(vals.size(), 5); 80 | 81 | for (size_t i = 0; i < vals.size(); ++i) { 82 | ASSERT_EQ(vals[i].second, i) << 83 | fmt::format("Values isn't equal on {} index", i); 84 | } 85 | } 86 | 87 | TEST(EmptyMapTest, SimpleSwap) { 88 | Map map; 89 | map[1] = 5; 90 | 91 | Map dict; 92 | dict[1] = 15; 93 | dict[2] = 14; 94 | 95 | size_t old_mp_size = map.Size(); 96 | size_t old_dict_size = dict.Size(); 97 | 98 | map.Swap(dict); 99 | 100 | ASSERT_EQ(dict.Size(), old_mp_size); 101 | ASSERT_EQ(map.Size(), old_dict_size); 102 | 103 | ASSERT_EQ(dict[1], 5); 104 | ASSERT_EQ(map[1], 15); 105 | ASSERT_EQ(map[2], 14); 106 | } 107 | 108 | TEST(EmptyMapTest, StdSwap) { 109 | Map map; 110 | map[1] = 5; 111 | 112 | Map dict; 113 | dict[1] = 15; 114 | dict[2] = 14; 115 | 116 | size_t old_mp_size = map.Size(); 117 | size_t old_dict_size = dict.Size(); 118 | 119 | std::swap(map, dict); 120 | 121 | ASSERT_EQ(dict.Size(), old_mp_size); 122 | ASSERT_EQ(map.Size(), old_dict_size); 123 | 124 | ASSERT_EQ(dict[1], 5); 125 | ASSERT_EQ(map[1], 15); 126 | ASSERT_EQ(map[2], 14); 127 | } 128 | 129 | TEST(EmptyMapTest, EraseOnlyRoot) { 130 | Map mp; 131 | mp.Insert({1, 2}); 132 | mp.Erase(1); 133 | ASSERT_EQ(mp.Size(), 0); 134 | 135 | auto vals = mp.Values(true); 136 | ASSERT_TRUE(vals.empty()); 137 | } 138 | 139 | TEST(EmptyMapTest, StringAsKey) { 140 | Map ages; 141 | ages.Insert({ 142 | {"Maxim", 21}, 143 | {"Danya", 22}, 144 | {"Veronika", 24}, 145 | {"Anna", 19} 146 | }); 147 | std::map std_ages{ 148 | {"Maxim", 21}, 149 | {"Danya", 22}, 150 | {"Veronika", 24}, 151 | {"Anna", 19} 152 | }; 153 | auto values = ages.Values(true); 154 | auto it = values.begin(); 155 | for (const auto& val: std_ages) { 156 | ASSERT_EQ(it->second, val.second) << 157 | fmt::format("Values isn't equal on {} index", 158 | std::distance(values.begin(), it) 159 | ); 160 | ++it; 161 | } 162 | } 163 | 164 | TEST_F(MapTest, GetValueUsingOperator) { 165 | ASSERT_EQ(mp[5], 90); 166 | ASSERT_EQ(mp[-10], 5); 167 | ASSERT_EQ(mp[1], 5); 168 | ASSERT_EQ(mp[0], 4); 169 | } 170 | 171 | TEST_F(MapTest, OverwritingWithOperator) { 172 | mp[5] = 5; 173 | mp[-10] = 10; 174 | ASSERT_EQ(mp[5], 5); 175 | ASSERT_EQ(mp[-10], 10); 176 | } 177 | 178 | TEST_F(MapTest, CreateIfNotExist) { 179 | mp[-1]; 180 | ASSERT_EQ(mp[-1], 0); // default for type value 181 | ASSERT_EQ(mp.Size(), sz + 1); 182 | } 183 | 184 | TEST_F(MapTest, GetIncreaseSortedValues) { 185 | auto values = mp.Values(true); 186 | 187 | for (size_t i = 1; i < values.size(); ++i) { 188 | ASSERT_LT(values[i - 1].first, values[i].first) << 189 | fmt::format("Doesn't increase starting with {} index", i); 190 | } 191 | } 192 | 193 | TEST_F(MapTest, GetDecreaseSortedValues) { 194 | auto values = mp.Values(false); 195 | 196 | for (size_t i = 1; i < values.size(); ++i) { 197 | ASSERT_GT(values[i - 1].first, values[i].first) << 198 | fmt::format("Doesn't decrease starting with {} index", i); 199 | } 200 | } 201 | 202 | TEST_F(MapTest, Clear) { 203 | mp.Clear(); 204 | ASSERT_TRUE(mp.IsEmpty()); 205 | ASSERT_EQ(mp.Size(), 0); 206 | } 207 | 208 | TEST_F(MapTest, FindExistValue) { 209 | ASSERT_TRUE(mp.Find(3)); 210 | } 211 | 212 | TEST_F(MapTest, FindNotExistValue) { 213 | ASSERT_FALSE(mp.Find(-11)); 214 | } 215 | 216 | TEST_F(MapTest, EraseLeaf) { 217 | mp.Erase(0); 218 | ASSERT_EQ(mp.Size(), sz - 1); 219 | 220 | auto vals = mp.Values(true); 221 | ASSERT_EQ(vals.size(), sz - 1); 222 | 223 | for (size_t i = 1; i < vals.size(); ++i) { 224 | ASSERT_NE(vals[i - 1].first, 0); 225 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 226 | fmt::format("Doesn't increase starting with {} index", i); 227 | } 228 | } 229 | 230 | TEST_F(MapTest, EraseNodeWithRightSon) { 231 | mp.Erase(-10); 232 | ASSERT_EQ(mp.Size(), sz - 1); 233 | 234 | auto vals = mp.Values(true); 235 | ASSERT_EQ(vals.size(), sz - 1); 236 | 237 | for (size_t i = 1; i < vals.size(); ++i) { 238 | ASSERT_NE(vals[i - 1].first, -10); 239 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 240 | fmt::format("Doesn't increase starting with {} index", i); 241 | } 242 | } 243 | 244 | TEST_F(MapTest, EraseNodeWithLeftSon) { 245 | mp.Erase(3); 246 | ASSERT_EQ(mp.Size(), sz - 1); 247 | 248 | auto vals = mp.Values(true); 249 | ASSERT_EQ(vals.size(), sz - 1); 250 | 251 | for (size_t i = 1; i < vals.size(); ++i) { 252 | ASSERT_NE(vals[i - 1].first, 3); 253 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 254 | fmt::format("Doesn't increase starting with {} index", i); 255 | } 256 | } 257 | 258 | TEST_F(MapTest, EraseNodeWithTwoSons) { 259 | mp.Erase(1); 260 | ASSERT_EQ(mp.Size(), sz - 1); 261 | 262 | auto vals = mp.Values(true); 263 | ASSERT_EQ(vals.size(), sz - 1); 264 | 265 | for (size_t i = 1; i < vals.size(); ++i) { 266 | ASSERT_NE(vals[i - 1].first, 1); 267 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 268 | fmt::format("Doesn't increase starting with {} index", i); 269 | } 270 | } 271 | 272 | TEST_F(MapTest, EraseSeveralValues) { 273 | mp.Erase(3); 274 | mp.Erase(-10); 275 | mp.Erase(0); 276 | ASSERT_EQ(mp.Size(), sz - 3); 277 | 278 | auto vals = mp.Values(true); 279 | ASSERT_EQ(vals.size(), sz - 3); 280 | 281 | for (size_t i = 1; i < vals.size(); ++i) { 282 | ASSERT_NE(vals[i - 1].first, 0); 283 | ASSERT_NE(vals[i - 1].first, 3); 284 | ASSERT_NE(vals[i - 1].first, -10); 285 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 286 | fmt::format("Doesn't increase starting with {} index", i); 287 | } 288 | } 289 | 290 | TEST_F(MapTest, EraseNotExistingValue) { 291 | EXPECT_ANY_THROW({ 292 | mp.Erase(-100); 293 | }); 294 | } 295 | 296 | TEST_F(MapTest, CustomComparator) { 297 | struct Point { 298 | int x; 299 | int y; 300 | bool operator==(const Point& b) const { 301 | return (this->x == b.x) && (this->y == b.y); 302 | } 303 | }; 304 | 305 | struct PointComparator { 306 | constexpr bool operator()(const Point& a, const Point& b) const { 307 | auto dist1 = sqrt(pow(a.x, 2) + pow(a.y, 2)); 308 | auto dist2 = sqrt(pow(b.x, 2) + pow(b.y, 2)); 309 | return dist1 < dist2; 310 | } 311 | }; 312 | 313 | Map points; 314 | points.Insert({ 315 | {{0, 0}, 21}, 316 | {{4, 5}, 22}, 317 | {{0, 10}, 24}, 318 | }); 319 | 320 | std::map std_points{ 321 | {{0, 0}, 21}, 322 | {{4, 5}, 22}, 323 | {{0, 10}, 24}, 324 | }; 325 | 326 | auto values = points.Values(true); 327 | auto it = values.begin(); 328 | for (const auto& val: std_points) { 329 | ASSERT_EQ(it->second, val.second) << 330 | fmt::format("Values isn't equal on {} index", 331 | std::distance(values.begin(), it) 332 | ); 333 | ++it; 334 | } 335 | 336 | } 337 | 338 | 339 | 340 | int main(int argc, char **argv) { 341 | ::testing::InitGoogleTest(&argc, argv); 342 | 343 | return RUN_ALL_TESTS(); 344 | } -------------------------------------------------------------------------------- /tasks/tree/iterators/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(map.hpp) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | add_task_test(stress_tests tests/stress.cpp) 5 | end_task() 6 | -------------------------------------------------------------------------------- /tasks/tree/iterators/map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template > 12 | class Map { 13 | 14 | class Node; 15 | 16 | public: 17 | class MapIterator { 18 | public: 19 | // NOLINTNEXTLINE 20 | using value_type = std::pair; 21 | // NOLINTNEXTLINE 22 | using reference_type = value_type&; 23 | // NOLINTNEXTLINE 24 | using pointer_type = value_type*; 25 | // NOLINTNEXTLINE 26 | using difference_type = std::ptrdiff_t; 27 | // NOLINTNEXTLINE 28 | using iterator_category = std::forward_iterator_tag; 29 | 30 | inline bool operator==(const MapIterator&) const { 31 | std::abort(); // Not implemented 32 | }; 33 | 34 | inline bool operator!=(const MapIterator&) const { 35 | std::abort(); // Not implemented 36 | }; 37 | 38 | inline reference_type operator*() const { 39 | std::abort(); // Not implemented 40 | }; 41 | 42 | MapIterator& operator++() { 43 | std::abort(); // Not implemented 44 | }; 45 | 46 | MapIterator operator++(int) { 47 | std::abort(); // Not implemented 48 | }; 49 | 50 | inline pointer_type operator->() const { 51 | std::abort(); // Not implemented 52 | }; 53 | 54 | private: 55 | explicit MapIterator(const Node*) { 56 | // Not implemented 57 | } 58 | 59 | private: 60 | Node* current_; 61 | }; 62 | 63 | inline MapIterator Begin() const noexcept { 64 | std::abort(); // Not implemented 65 | } 66 | 67 | inline MapIterator End() const noexcept { 68 | std::abort(); // Not implemented 69 | } 70 | 71 | Map() { 72 | // Not implemented 73 | } 74 | 75 | Value& operator[](const Key& /*key*/) { 76 | std::abort(); // Not implemented 77 | } 78 | 79 | inline bool IsEmpty() const noexcept { 80 | std::abort(); // Not implemented 81 | } 82 | 83 | inline size_t Size() const noexcept { 84 | std::abort(); // Not implemented 85 | } 86 | 87 | void Swap(Map& a) { 88 | static_assert(std::is_samecomp), decltype(a.comp)>::value, 89 | "The compare function types are different"); 90 | // Not implemented 91 | } 92 | 93 | std::vector> Values(bool /*is_increase=true*/) const noexcept { 94 | std::abort(); // Not implemented 95 | } 96 | 97 | void Insert(const std::pair& /*val*/) { 98 | // Not implemented 99 | } 100 | 101 | void Insert(const std::initializer_list>& /*values*/) { 102 | // Not implemented 103 | } 104 | 105 | void Erase(const Key& /*key*/) { 106 | // Not implemented 107 | } 108 | 109 | void Clear() noexcept { 110 | // Not implemented 111 | } 112 | 113 | MapIterator Find(const Key& /*key*/) const { 114 | std::abort(); // Not implemented 115 | } 116 | 117 | ~Map() { 118 | // Not implemented 119 | } 120 | 121 | private: 122 | class Node { 123 | friend class MapIterator; 124 | friend class Map; 125 | 126 | private: 127 | /*???*/ 128 | }; 129 | /*???*/ 130 | 131 | private: 132 | Compare comp; 133 | /*???*/ 134 | }; 135 | 136 | namespace std { 137 | // Global swap overloading 138 | template 139 | void swap(Map& a, Map& b) { 140 | a.Swap(b); 141 | } 142 | } // namespace std -------------------------------------------------------------------------------- /tasks/tree/iterators/readme.md: -------------------------------------------------------------------------------- 1 | # Прошивка деревьев. Итераторы 2 | 3 | ## Пререквизиты 4 | 5 | - [tree/bst](/tasks/tree/bst) 6 | 7 | --- 8 | 9 | В этой задаче изменим свой `Map`, добавив в него итераторы. 10 | 11 | ## Дизайн 12 | 13 | Классическим для списков дизайном итераторов является введение `Fake_Node` - служебной ноды, которая `не хранит в себе пользовательские данные!` 14 | 15 | Для простоты мы будем реализовывать однонаправленные итераторы, позволяющие двигаться только вперед. 16 | 17 | Есть три способа реализации итераторов: 18 | 19 | ### 1. Указатель на родителя 20 | Зная указатель на родителя мы сможем лекго перемещаться вверх - вниз по дереву. Удобно, но жалко 8 байт для каждой ноды. 21 | 22 | ### 2. Прошивка (Threads) 23 | 24 | Суть прошивки проста - заменить все правые nullptr на указатели на следующий по порядку узел. 25 | 26 | Если мы хотим поддержать операции декримента, то аналогично нужно поддержать пустые левые указатели на предыдущий по порядку узел. 27 | 28 | Прошитые деревья используют более сложные алгоритмы вставки и удаления, чтобы избежать затрат на хранение при добавлении родительских указателей. 29 | 30 | ### 3. Корутины 31 | 32 | Продвинутый уровень. Было бы классно иметь классическую функцию InOrder Bypass, которая при операции ++ `останавливалась`, а при повторном вызове функции вызывалась ровно с того места, где остановилась, сохранив при этом весь стек вызовов. Это и есть идея корутин. Например, ключевое слово `yield` в Python позвоялет это делать. Выглядело бы как-то так: 33 | ```c++ 34 | void InOrderBypass(Node* node) { 35 | if (node == nullptr) return; 36 | InOrderBypass(node->left); 37 | YIELD; 38 | InOrderBypass(node->right); 39 | } 40 | ``` 41 | 42 | 43 | ## Алгоритм прошивки дерева 44 | В класс `Node` добавиться булевская переменная, показыващая является ли правый указатель нитью. 45 | 46 | Как прошить готовое бинарое дерево поиска? Для этого будем использовать `std::stack`. Псевдокод будет выглядеть так: 47 | ```C++ 48 | if (node->right){ 49 | is_right_link = false; 50 | st.push(node); 51 | } else { 52 | is_right_link = true; 53 | node->right = st.top(); 54 | st.pop(); 55 | if (node->left) { 56 | st.push(node); 57 | } 58 | } 59 | ``` 60 | 61 | ## Про итераторы 62 | 63 | Итераторы обходят дерево в порядке возрастания элементов. Следовательно: 64 | `Begin()` - возвращается итератор на минимальный элемент в дереве. Работает за O(logN) 65 | `End()` - возвращает итератор на Fake_node. Работает за O(1) 66 | 67 | ### `operator++` 68 | Если текущая node не была помечена как нить - идите максимально влево. 69 | 70 | Иначе идите вправо. 71 | 72 | --- 73 | ### `Find` 74 | Теперь мы возвращаем пользователю итератор на найденный элемент. Если элемента нет - возвращаем `End()` 75 | 76 | ## Задание 77 | 78 | Измените [словарь](map.hpp), добавив в него итераторы с помощью прошивки дерева. Постарайтесь сохранять прошитость после вставки и удаления элементов из дерева. 79 | 80 | ### Указания к реализации 81 | 82 | `Запрещено хранить указатель на родителя в Node!` 83 | 84 | **В публичное API не стоит добавлять новых методов!** 85 | 86 | **В публичном API не должно быть класса `Node`!** -------------------------------------------------------------------------------- /tasks/tree/iterators/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": ["map.hpp"], 18 | "submit_files": ["map.hpp"], 19 | "forbidden": [ 20 | { 21 | "patterns": [ 22 | "Not implemented" 23 | ], 24 | "hint": "You should implement this part" 25 | }, 26 | { 27 | "patterns": [ 28 | "std::map" 29 | ], 30 | "hint": "Don't use std::map -> implement him" 31 | }, 32 | { 33 | "patterns": [ 34 | "std::sort" 35 | ], 36 | "hint": "Don't use std::sort -> BST doesn't need this" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tasks/tree/iterators/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../map.hpp" 9 | 10 | void ConstructRandomMap(Map& mp, int sz) { 11 | std::random_device rd; 12 | std::mt19937 mt(rd()); 13 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 14 | int random_key; 15 | while(sz) { 16 | random_key = dist(mt); 17 | mp.Insert(std::pair{random_key, 1}); 18 | --sz; 19 | } 20 | } 21 | 22 | void ConstructRandomMap(std::map& mp, int sz) { 23 | std::random_device rd; 24 | std::mt19937 mt(rd()); 25 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 26 | int random_key; 27 | while(sz) { 28 | random_key = dist(mt); 29 | mp.insert(std::pair{random_key, 1}); 30 | --sz; 31 | } 32 | } 33 | 34 | void ConstructLinearMap(Map& mp, int sz) { 35 | while(sz) { 36 | mp.Insert(std::pair{sz, 1}); 37 | --sz; 38 | } 39 | } 40 | 41 | void ConstructLinearMap(std::map& mp, int sz) { 42 | while(sz) { 43 | mp.insert(std::pair{sz, 1}); 44 | --sz; 45 | } 46 | } 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | void BM_CustomMapRandomInsert(benchmark::State& state) { 50 | Map mp; 51 | for (auto _ : state) { 52 | ConstructRandomMap(mp, state.range(0)); 53 | } 54 | state.SetComplexityN(state.range(0)); 55 | } 56 | 57 | void BM_StdMapRandomInsert(benchmark::State& state) { 58 | std::map mp; 59 | for (auto _ : state) { 60 | ConstructRandomMap(mp, state.range(0)); 61 | } 62 | state.SetComplexityN(state.range(0)); 63 | } 64 | 65 | void BM_CustomMapLinearInsert(benchmark::State& state) { 66 | Map mp; 67 | for (auto _ : state) { 68 | ConstructLinearMap(mp, state.range(0)); 69 | } 70 | state.SetComplexityN(state.range(0)); 71 | } 72 | 73 | void BM_StdMapLinearInsert(benchmark::State& state) { 74 | std::map mp; 75 | for (auto _ : state) { 76 | ConstructLinearMap(mp, state.range(0)); 77 | } 78 | state.SetComplexityN(state.range(0)); 79 | } 80 | 81 | void BM_CustomMapErase(benchmark::State& state) { 82 | Map mp; 83 | std::random_device rd; 84 | std::mt19937 mt(rd()); 85 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 86 | int random_key; 87 | for (auto _ : state) { 88 | ConstructRandomMap(mp, state.range(0)); 89 | for (int64_t i = 0; i < state.range(0); ++i) { 90 | random_key = dist(mt); 91 | try{ 92 | mp.Erase(random_key); 93 | } catch(...){} 94 | } 95 | } 96 | state.SetComplexityN(state.range(0)); 97 | } 98 | 99 | void BM_StdMapErase(benchmark::State& state) { 100 | std::map mp; 101 | std::random_device rd; 102 | std::mt19937 mt(rd()); 103 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 104 | int random_key; 105 | for (auto _ : state) { 106 | ConstructRandomMap(mp, state.range(0)); 107 | for (int64_t i = 0; i < state.range(0); ++i) { 108 | random_key = dist(mt); 109 | try{ 110 | mp.erase(random_key); 111 | } catch(...){} 112 | } 113 | } 114 | state.SetComplexityN(state.range(0)); 115 | } 116 | 117 | void BM_CustomMapClear(benchmark::State& state) { 118 | Map mp; 119 | for (auto _ : state) { 120 | ConstructRandomMap(mp, state.range(0)); 121 | mp.Clear(); 122 | } 123 | state.SetComplexityN(state.range(0)); 124 | } 125 | 126 | void BM_StdMapClear(benchmark::State& state) { 127 | std::map mp; 128 | for (auto _ : state) { 129 | ConstructRandomMap(mp, state.range(0)); 130 | mp.clear(); 131 | } 132 | state.SetComplexityN(state.range(0)); 133 | } 134 | 135 | 136 | BENCHMARK(BM_CustomMapRandomInsert)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 137 | BENCHMARK(BM_StdMapRandomInsert)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 138 | BENCHMARK(BM_CustomMapLinearInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 139 | BENCHMARK(BM_StdMapLinearInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 140 | BENCHMARK(BM_CustomMapErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 141 | BENCHMARK(BM_StdMapErase)->Range(1<<10, 1<<17)->Complexity()->Unit(benchmark::kMillisecond); 142 | BENCHMARK(BM_CustomMapClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 143 | BENCHMARK(BM_StdMapClear)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 144 | 145 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/tree/iterators/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "../map.hpp" 12 | 13 | class MapTest: public testing::Test { 14 | protected: 15 | void SetUp() override { 16 | mp.Insert({ 17 | {1, 5}, 18 | {3, 10}, 19 | {5, 90}, 20 | {10, -10}, 21 | {90, 0}, 22 | {-10, 5}, 23 | {0, 4} 24 | }); 25 | assert(mp.Size() == sz); 26 | } 27 | 28 | Map mp; 29 | const size_t sz = 7; 30 | }; 31 | 32 | 33 | TEST(EmptyMapTest, DefaultConstructor) { 34 | Map map; 35 | ASSERT_TRUE(map.IsEmpty()) << "Default Map isn't empty!"; 36 | } 37 | 38 | TEST(EmptyMapTest, InsertRoot) { 39 | Map map; 40 | map.Insert({1, 5}); 41 | ASSERT_EQ(map.Size(), 1); 42 | 43 | auto vals = map.Values(true); 44 | 45 | ASSERT_EQ(vals.size(), 1); 46 | ASSERT_EQ(vals[0].first, 1); 47 | ASSERT_EQ(vals[0].second, 5); 48 | } 49 | 50 | TEST(EmptyMapTest, InsertRootLeftRight) { 51 | Map map; 52 | map.Insert({1, 1}); 53 | map.Insert({3, 2}); 54 | map.Insert({0, 0}); 55 | 56 | ASSERT_EQ(map.Size(), 3); 57 | 58 | auto vals = map.Values(true); 59 | ASSERT_EQ(vals.size(), 3); 60 | 61 | for (size_t i = 0; i < vals.size(); ++i) { 62 | ASSERT_EQ(vals[i].second, i) << 63 | fmt::format("Values isn't equal on {} index", i); 64 | } 65 | } 66 | 67 | TEST(EmptyMapTest, InsertIncreaseSeq) { 68 | Map map; 69 | map.Insert({ 70 | {1, 0}, 71 | {3, 1}, 72 | {5, 2}, 73 | {10, 3}, 74 | {90, 4} 75 | }); 76 | ASSERT_EQ(map.Size(), 5); 77 | 78 | auto vals = map.Values(true); 79 | ASSERT_EQ(vals.size(), 5); 80 | 81 | for (size_t i = 0; i < vals.size(); ++i) { 82 | ASSERT_EQ(vals[i].second, i) << 83 | fmt::format("Values isn't equal on {} index", i); 84 | } 85 | } 86 | 87 | TEST(EmptyMapTest, SimpleSwap) { 88 | Map map; 89 | map[1] = 5; 90 | 91 | Map dict; 92 | dict[1] = 15; 93 | dict[2] = 14; 94 | 95 | size_t old_mp_size = map.Size(); 96 | size_t old_dict_size = dict.Size(); 97 | 98 | map.Swap(dict); 99 | 100 | ASSERT_EQ(dict.Size(), old_mp_size); 101 | ASSERT_EQ(map.Size(), old_dict_size); 102 | 103 | ASSERT_EQ(dict[1], 5); 104 | ASSERT_EQ(map[1], 15); 105 | ASSERT_EQ(map[2], 14); 106 | } 107 | 108 | TEST(EmptyMapTest, StdSwap) { 109 | Map map; 110 | map[1] = 5; 111 | 112 | Map dict; 113 | dict[1] = 15; 114 | dict[2] = 14; 115 | 116 | size_t old_mp_size = map.Size(); 117 | size_t old_dict_size = dict.Size(); 118 | 119 | std::swap(map, dict); 120 | 121 | ASSERT_EQ(dict.Size(), old_mp_size); 122 | ASSERT_EQ(map.Size(), old_dict_size); 123 | 124 | ASSERT_EQ(dict[1], 5); 125 | ASSERT_EQ(map[1], 15); 126 | ASSERT_EQ(map[2], 14); 127 | } 128 | 129 | TEST(EmptyMapTest, EraseOnlyRoot) { 130 | Map mp; 131 | mp.Insert({1, 2}); 132 | mp.Erase(1); 133 | ASSERT_EQ(mp.Size(), 0); 134 | 135 | auto vals = mp.Values(true); 136 | ASSERT_TRUE(vals.empty()); 137 | } 138 | 139 | TEST(EmptyMapTest, StringAsKey) { 140 | Map ages; 141 | ages.Insert({ 142 | {"Maxim", 21}, 143 | {"Danya", 22}, 144 | {"Veronika", 24}, 145 | {"Anna", 19} 146 | }); 147 | std::map std_ages{ 148 | {"Maxim", 21}, 149 | {"Danya", 22}, 150 | {"Veronika", 24}, 151 | {"Anna", 19} 152 | }; 153 | auto values = ages.Values(true); 154 | auto it = values.begin(); 155 | for (const auto& val: std_ages) { 156 | ASSERT_EQ(it->second, val.second) << 157 | fmt::format("Values isn't equal on {} index", 158 | std::distance(values.begin(), it) 159 | ); 160 | ++it; 161 | } 162 | } 163 | 164 | TEST(EmptyMapTest, IteratorsEmptyTree) { 165 | Map mp; 166 | ASSERT_EQ(mp.Begin(), mp.End()); 167 | } 168 | 169 | TEST(EmptyMapTest, TreeFromExample) { 170 | Map mp; 171 | mp.Insert({ 172 | {10, 10}, 173 | {8, 8}, 174 | {9, 9}, 175 | {13, 13}, 176 | {11, 11}, 177 | {7, 7}, 178 | {6, 6}, 179 | }); 180 | auto values = mp.Values(true); 181 | auto it = mp.Begin(); 182 | for (size_t i = 0; i < values.size(); ++i) { 183 | ASSERT_EQ(it->second, values[i].second) << 184 | fmt::format("Values isn't equal on {} index", 185 | i 186 | ); 187 | ++it; 188 | } 189 | } 190 | 191 | TEST_F(MapTest, GetValueUsingOperator) { 192 | ASSERT_EQ(mp[5], 90); 193 | ASSERT_EQ(mp[-10], 5); 194 | ASSERT_EQ(mp[1], 5); 195 | ASSERT_EQ(mp[0], 4); 196 | } 197 | 198 | TEST_F(MapTest, OverwritingWithOperator) { 199 | mp[5] = 5; 200 | mp[-10] = 10; 201 | ASSERT_EQ(mp[5], 5); 202 | ASSERT_EQ(mp[-10], 10); 203 | } 204 | 205 | TEST_F(MapTest, CreateIfNotExist) { 206 | mp[-1]; 207 | ASSERT_EQ(mp[-1], 0); // default for type value 208 | ASSERT_EQ(mp.Size(), sz + 1); 209 | } 210 | 211 | TEST_F(MapTest, GetIncreaseSortedValues) { 212 | auto values = mp.Values(true); 213 | 214 | for (size_t i = 1; i < values.size(); ++i) { 215 | ASSERT_LT(values[i - 1].first, values[i].first) << 216 | fmt::format("Doesn't increase starting with {} index", i); 217 | } 218 | } 219 | 220 | TEST_F(MapTest, GetDecreaseSortedValues) { 221 | auto values = mp.Values(false); 222 | 223 | for (size_t i = 1; i < values.size(); ++i) { 224 | ASSERT_GT(values[i - 1].first, values[i].first) << 225 | fmt::format("Doesn't decrease starting with {} index", i); 226 | } 227 | } 228 | 229 | TEST_F(MapTest, SimpleIteratorBypass) { 230 | auto values = mp.Values(true); 231 | auto it = mp.Begin(); 232 | 233 | for (size_t i = 0; i < values.size(); ++i, it++) { 234 | ASSERT_EQ(*it, values[i]); 235 | } 236 | } 237 | 238 | TEST_F(MapTest, Clear) { 239 | mp.Clear(); 240 | ASSERT_TRUE(mp.IsEmpty()); 241 | ASSERT_EQ(mp.Size(), 0); 242 | } 243 | 244 | TEST_F(MapTest, FindExistValue) { 245 | ASSERT_NE(mp.Find(3), mp.End()); 246 | } 247 | 248 | TEST_F(MapTest, FindNotExistValue) { 249 | ASSERT_EQ(mp.Find(-11), mp.End()); 250 | } 251 | 252 | TEST_F(MapTest, EraseLeaf) { 253 | mp.Erase(0); 254 | ASSERT_EQ(mp.Size(), sz - 1); 255 | 256 | auto vals = mp.Values(true); 257 | ASSERT_EQ(vals.size(), sz - 1); 258 | 259 | for (size_t i = 1; i < vals.size(); ++i) { 260 | ASSERT_NE(vals[i - 1].first, 0); 261 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 262 | fmt::format("Doesn't increase starting with {} index", i); 263 | } 264 | } 265 | 266 | TEST_F(MapTest, EraseNodeWithRightSon) { 267 | mp.Erase(-10); 268 | ASSERT_EQ(mp.Size(), sz - 1); 269 | 270 | auto vals = mp.Values(true); 271 | ASSERT_EQ(vals.size(), sz - 1); 272 | 273 | for (size_t i = 1; i < vals.size(); ++i) { 274 | ASSERT_NE(vals[i - 1].first, -10); 275 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 276 | fmt::format("Doesn't increase starting with {} index", i); 277 | } 278 | } 279 | 280 | TEST_F(MapTest, EraseNodeWithLeftSon) { 281 | mp.Erase(3); 282 | ASSERT_EQ(mp.Size(), sz - 1); 283 | 284 | auto vals = mp.Values(true); 285 | ASSERT_EQ(vals.size(), sz - 1); 286 | 287 | for (size_t i = 1; i < vals.size(); ++i) { 288 | ASSERT_NE(vals[i - 1].first, 3); 289 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 290 | fmt::format("Doesn't increase starting with {} index", i); 291 | } 292 | } 293 | 294 | TEST_F(MapTest, EraseNodeWithTwoSons) { 295 | mp.Erase(1); 296 | ASSERT_EQ(mp.Size(), sz - 1); 297 | 298 | auto vals = mp.Values(true); 299 | ASSERT_EQ(vals.size(), sz - 1); 300 | 301 | for (size_t i = 1; i < vals.size(); ++i) { 302 | ASSERT_NE(vals[i - 1].first, 1); 303 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 304 | fmt::format("Doesn't increase starting with {} index", i); 305 | } 306 | } 307 | 308 | TEST_F(MapTest, EraseSeveralValues) { 309 | mp.Erase(3); 310 | mp.Erase(-10); 311 | mp.Erase(0); 312 | ASSERT_EQ(mp.Size(), sz - 3); 313 | 314 | auto vals = mp.Values(true); 315 | ASSERT_EQ(vals.size(), sz - 3); 316 | 317 | for (size_t i = 1; i < vals.size(); ++i) { 318 | ASSERT_NE(vals[i - 1].first, 0); 319 | ASSERT_NE(vals[i - 1].first, 3); 320 | ASSERT_NE(vals[i - 1].first, -10); 321 | ASSERT_LT(vals[i - 1].first, vals[i].first) << 322 | fmt::format("Doesn't increase starting with {} index", i); 323 | } 324 | } 325 | 326 | TEST_F(MapTest, EraseNotExistingValue) { 327 | EXPECT_ANY_THROW({ 328 | mp.Erase(-100); 329 | }); 330 | } 331 | 332 | TEST_F(MapTest, CustomComparator) { 333 | struct Point { 334 | int x; 335 | int y; 336 | bool operator==(const Point& b) const { 337 | return (this->x == b.x) && (this->y == b.y); 338 | } 339 | }; 340 | 341 | struct PointComparator { 342 | constexpr bool operator()(const Point& a, const Point& b) const { 343 | auto dist1 = sqrt(pow(a.x, 2) + pow(a.y, 2)); 344 | auto dist2 = sqrt(pow(b.x, 2) + pow(b.y, 2)); 345 | return dist1 < dist2; 346 | } 347 | }; 348 | 349 | Map points; 350 | points.Insert({ 351 | {{0, 0}, 21}, 352 | {{4, 5}, 22}, 353 | {{0, 10}, 24}, 354 | }); 355 | 356 | std::map std_points{ 357 | {{0, 0}, 21}, 358 | {{4, 5}, 22}, 359 | {{0, 10}, 24}, 360 | }; 361 | 362 | auto values = points.Values(true); 363 | auto it = values.begin(); 364 | for (const auto& val: std_points) { 365 | ASSERT_EQ(it->second, val.second) << 366 | fmt::format("Values isn't equal on {} index", 367 | std::distance(values.begin(), it) 368 | ); 369 | ++it; 370 | } 371 | 372 | } 373 | 374 | 375 | 376 | int main(int argc, char **argv) { 377 | ::testing::InitGoogleTest(&argc, argv); 378 | 379 | return RUN_ALL_TESTS(); 380 | } -------------------------------------------------------------------------------- /tasks/tree/readme.md: -------------------------------------------------------------------------------- 1 | # Деревья 2 | 3 | ## Дерево 4 | 5 | `Дерево` - это ациклический, связанный [граф](https://ru.wikipedia.org/wiki/Граф_(математика)), где каждая вершина может иметь несколько связей с другими вершинами. 6 | 7 | Аналогия: файловая система или родословное дерево. 8 | 9 | ## Определения 10 | 11 | #### Root (корень) 12 | Cтартовая вершина с которого начинается дерево. 13 | 14 | #### Node (узел) 15 | Производный элемент от корня. Сам корень также является узлом. 16 | 17 | #### Parent (родитель) 18 | Узел, от которого создан текущий узел. У корня нет родителя по определению. 19 | 20 | _Так как дерево часто рассматривают как родословное, то все сын/дочь, отец/мать, брат/сестра, дядя/тётя определяются аналогично родословным связям_ 21 | 22 | #### Leaf (лист) 23 | Узел, не имеющий детей. 24 | 25 | #### Глубина дерева 26 | Максимальная длина пути от корня до листа. 27 | 28 | #### Глубина вершины 29 | Длина пути от корня до вершины. 30 | 31 | Краевым случаем тут является путь от корня до корня. Логически верно сказать, что он равен 0. 32 | Однако если в дереве есть только root, то глубина дерева равна 1. 33 | 34 | В литературе/статьях используются оба варианта, но чаще всего принимают глубину как 0. 35 | 36 | #### Степень вершины 37 | Количество детей 38 | 39 | #### Ширина уровня 40 | Кол-во узлом на данной высоте. Ширина дерева - максимальная ширина среди всех уровней. 41 | 42 | ## Задания 43 | 44 | - [Бинарное дерево поиска](bst) 45 | - [Итераторы деревьев](iterators) 46 | - [Файловая система](NTree) 47 | 48 | -------------------------------------------------------------------------------- /tasks/tutorial/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | add_subdirectory(aplusb) 4 | -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | set_task_sources(aplusb.hpp) 3 | add_task_test(unit_tests tests/unit.cpp) 4 | add_task_test(stress_tests tests/stress.cpp) 5 | end_task() 6 | -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/aplusb.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int Sum(int /*a*/, int /*b*/) { 4 | std::abort(); // Not implemented. 5 | } 6 | -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/readme.md: -------------------------------------------------------------------------------- 1 | # A+B 2 | 3 | Реализуйте функцию `Sum`. 4 | 5 | ## Работа с задачей 6 | 7 | Для запуска тестов выполните в директории с задачей команду `clippy test`. 8 | 9 | Для запуска конкретной тестовой цели CMake выполните в директории с задачей команду `clippy target {target-name} {build-profile-name}`. 10 | 11 | Для автоматического форматирования кода задачи с помощью [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) выполните команду `clippy format`. Конфиг для `clang-format` находится в корне репозитория в файле [`.clang-format`](/.clang-format). 12 | 13 | Для проверки кода с помощью линтера [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/) выполните команду `clippy tidy`. Конфиг для `clang-tidy` находится в корне репозитория в файле [`.clang-tidy`](/.clang-tidy). 14 | 15 | Разрешается менять только файлы / директории, перечисленные в поле `submit_files` конфига задачи [`task.json`](task.json). Только эти файлы, как следует из названия поля, будут отправлены на проверку. 16 | 17 | В CI на решении запускается команда `clippy validate` для проверки качества кода и запрещенных паттернов. 18 | -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": ["aplusb.hpp"], 18 | "submit_files": ["aplusb.hpp"], 19 | "forbidden": [ 20 | { 21 | "patterns": [ 22 | "Not implemented" 23 | ], 24 | "hint": "You should implement this part" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "../aplusb.hpp" 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | void BM_CustomSum(benchmark::State& state) { 9 | for (auto _ : state) { 10 | for (int i = 0; i < state.range(0); ++i) { 11 | Sum(i, i); 12 | } 13 | } 14 | state.SetComplexityN(state.range(0)); 15 | } 16 | 17 | void BM_MutByTwo(benchmark::State& state) { 18 | auto f = [](int x) { 19 | return x * 2; 20 | }; 21 | 22 | for (auto _ : state) { 23 | for (int i = 0; i < state.range(0); ++i) { 24 | f(i); 25 | } 26 | } 27 | state.SetComplexityN(state.range(0)); 28 | } 29 | 30 | 31 | BENCHMARK(BM_CustomSum)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 32 | BENCHMARK(BM_MutByTwo)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 33 | 34 | 35 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/tutorial/aplusb/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../aplusb.hpp" 4 | 5 | 6 | TEST(Sum, Simple) { 7 | ASSERT_EQ(Sum(1, 1), 2); 8 | } 9 | 10 | TEST(Sum, Zero) { 11 | ASSERT_EQ(Sum(0, 0), 0); 12 | } 13 | 14 | TEST(Sum, NegativeAndPositive) { 15 | ASSERT_EQ(Sum(-1, 1), 0); 16 | } 17 | 18 | TEST(Sum, TwoNegative) { 19 | ASSERT_EQ(Sum(-1, -1), -2); 20 | } 21 | 22 | TEST(Sum, Overflow) { 23 | ASSERT_EQ(Sum(INT_MAX, 1), INT_MAX); 24 | } 25 | 26 | TEST(Sum, Fun) { 27 | ASSERT_EQ(Sum(INT_MAX, INT_MIN + 1), 0); 28 | } 29 | 30 | int main(int argc, char **argv) { 31 | ::testing::InitGoogleTest(&argc, argv); 32 | 33 | return RUN_ALL_TESTS(); 34 | } -------------------------------------------------------------------------------- /tasks/vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | add_subdirectory(vector) -------------------------------------------------------------------------------- /tasks/vector/readme.md: -------------------------------------------------------------------------------- 1 | # Линейные списки 2 | 3 | - [Вектор](vector) 4 | -------------------------------------------------------------------------------- /tasks/vector/vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | begin_task() 2 | 3 | set_task_sources(vector.cpp) 4 | 5 | add_task_test(unit_tests tests/unit.cpp) 6 | 7 | add_task_test(stress_tests tests/stress.cpp) 8 | 9 | end_task() 10 | -------------------------------------------------------------------------------- /tasks/vector/vector/readme.md: -------------------------------------------------------------------------------- 1 | # Вектор 2 | _"Как проверить, что человек знает C++? Попросить его написть свой std::vector"_ – Илья Мещерин. 3 | 4 | ## Пререквизиты 5 | 6 | - [lists/list](/tasks/lists/list) 7 | - [tree/bst](/tasks/tree/bst) 8 | --- 9 | 10 | В этой задаче напишем свой [std::vector](https://en.cppreference.com/w/cpp/container/vector). 11 | 12 | --- 13 | 14 | *vector* – структура данных, в которой последовательно хранятся элементы одного типа. Другое название - массив динамической длины. 15 | 16 | ## Сложность операций 17 | 18 | Вектор позволяет вставлять элемент в конец в среднем за O(1), искать за O(N), получать доступ к элементу за O(1). Удаление происходит за O(N). 19 | 20 | ## Capacity и Size 21 | `Capacity` (объём) показывает сколько элементов может быть вставлено в буфер данных. 22 | 23 | `Size` (размер) показывает, сколько элементов лежит в буфере сейчас. 24 | 25 | ## Реаллокации (realloc) 26 | Зачастую необходимо увеличить размер буфера, сохранив при этом текущие данные. Для этого придётся выделить буфер в два раза больше текущего, переложить туда элементы и удалить старый буфер. 27 | 28 | ## Placement new 29 | 30 | Напомню работу обычного оператора `new`: 31 | 1) Выделить память размера size 32 | 2) Вызвать конструктор на эту память. 33 | 34 | Т.е. по итогу вы получаете готовые объекты. 35 | 36 | Это не всегда то, чего мы хотим. Что, если в конструкторе у нас происходит захват ресурсов: соединения к базе данных, память, общие данные в мьютексе? В таком случае нам нужно просто выделить память (malloc), а дальше, когда пользователь захочет, сконструировать на эту память объект. Делается это при помощи операции [placement new](https://www.geeksforgeeks.org/placement-new-operator-cpp/) 37 | 38 | ## Копирование 39 | При реаллокации необходимо скопировать элементы на уже выделенную память. 40 | 41 | Правильным решением будет использовать [`std::uninitialized_copy`](https://en.cppreference.com/w/cpp/memory/uninitialized_copy), которая копирует элементы на уже выделенную сырую память. 42 | 43 | ## EmplaceBack 44 | 45 | Если PushBack копирует существующий элемент в конец вектора, либо перемещает его туда при помощи `std::move`, то EmplaceBack сразу конструирует объект в векторе. Для этого метод принимает параметры для конструктора объекта при помощи шаблонов переменной длины. Обратите внимание, что параметры принимаются по универсальной ссылке! 46 | 47 | ## PushBack 48 | 49 | Обратите внимание, что PushBack принимает параметры `по значению`. Это разумное поведение: пользователь либо хочет скопировать объект в вектор, либо переместить туда. В обоих случаях вызовется эта версия. 50 | 51 | ### Почему push_back за O(1), если у нас есть реаллокации стоимостью O(N)? 52 | 53 | Важно, что O(1) `в среднем!` 54 | 55 | [Доказательство, что push_back() работает в среднем за O(1)](https://cs.stackexchange.com/questions/9380/why-is-push-back-in-c-vectors-constant-amortized) 56 | 57 | ## delete[] 58 | 59 | Напомню, как работает `delete[]`: 60 | 1) Вызвать деструкторы объектов по всему массиву 61 | 2) Вызвать `free` на массив 62 | 63 | В нашем случае на массив чаще всего выделено больше памяти, чем в нём хранится элементов (`capacity >= size`). Как было описано выше, мы не хотим создавать объект при выделении памяти - создаём только когда пользовать пожелает вставить элемент. 64 | 65 | Следовательно, только size элементов в нашем массиве - это объекты. Остальное - мусор. Вызвать деструктор по мусору - UB. 66 | 67 | Иначе говоря, `delete[]` - нам не подходит. Подумайте, как сделать правильно. 68 | 69 | ## Задание 70 | 71 | Напишите реализацию [Vector](vector.hpp). 72 | 73 | Чтобы лучше понять ожидаемое поведение, посмотрите тесты. 74 | 75 | ## Указания к реализации 76 | 77 | При первом добавлении элемента стоит выделить capacity = 10. 78 | 79 | Если в Reserve подаётся размер меньший или равный текущему capacity, то функция не имеет никакого эффекта. 80 | 81 | `Erase(start, end)` соответствует удалению элементов из отрезка `[start, end)` -------------------------------------------------------------------------------- /tasks/vector/vector/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "targets": ["unit_tests"], 5 | "profiles": [ 6 | "Debug", 7 | "DebugASan" 8 | ] 9 | }, 10 | { 11 | "targets": ["stress_tests"], 12 | "profiles": [ 13 | "Release" 14 | ] 15 | } 16 | ], 17 | "lint_files": [ 18 | "vector.hpp", 19 | "vector.cpp" 20 | ], 21 | "submit_files": ["vector.hpp", "vector.cpp"], 22 | "forbidden": [ 23 | { 24 | "patterns": [ 25 | "Not implemented" 26 | ], 27 | "hint": "You should implement this part" 28 | }, 29 | { 30 | "patterns": [ 31 | "std::vector" 32 | ], 33 | "hint": "Don't use std::vector -> implement him" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tasks/vector/vector/tests/stress.cpp: -------------------------------------------------------------------------------- 1 | #include "../vector.hpp" 2 | #include "../vector.cpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | void ConstructRandomVector(Vector& vec, int sz) { 12 | std::random_device rd; 13 | std::mt19937 mt(rd()); 14 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 15 | int random_key; 16 | while(sz) { 17 | random_key = dist(mt); 18 | vec.PushBack(random_key); 19 | --sz; 20 | } 21 | } 22 | 23 | void ConstructRandomVector(std::vector& vec, int sz) { 24 | std::random_device rd; 25 | std::mt19937 mt(rd()); 26 | std::uniform_int_distribution dist(INT_MIN, INT_MAX); 27 | int random_key; 28 | while(sz) { 29 | random_key = dist(mt); 30 | vec.push_back(random_key); 31 | --sz; 32 | } 33 | } 34 | 35 | //////////////////////////////////////////////////////////////////////////////// 36 | void BM_CustomVectorPushBack(benchmark::State& state) { 37 | Vector vec; 38 | for (auto _ : state) { 39 | ConstructRandomVector(vec, state.range(0)); 40 | } 41 | state.SetComplexityN(state.range(0)); 42 | } 43 | 44 | void BM_StdVectorPushBack(benchmark::State& state) { 45 | std::vector vec; 46 | for (auto _ : state) { 47 | ConstructRandomVector(vec, state.range(0)); 48 | } 49 | state.SetComplexityN(state.range(0)); 50 | } 51 | 52 | void BM_CustomVectorMiddleInsert(benchmark::State& state) { 53 | Vector vec; 54 | ConstructRandomVector(vec, 100); 55 | for (auto _ : state) { 56 | for (int i = 0; i < state.range(0); ++i){ 57 | vec.Insert(vec.Size() / 2, 50); 58 | } 59 | } 60 | state.SetComplexityN(state.range(0)); 61 | } 62 | 63 | void BM_StdVectorMiddleInsert(benchmark::State& state) { 64 | std::vector vec; 65 | ConstructRandomVector(vec, 100); 66 | auto it = vec.begin(); 67 | for (auto _ : state) { 68 | for (int i = 0; i < state.range(0); ++i){ 69 | it = vec.begin(); 70 | std::advance(it, vec.size() / 2); 71 | vec.insert(it, 50); 72 | } 73 | } 74 | state.SetComplexityN(state.range(0)); 75 | } 76 | 77 | 78 | BENCHMARK(BM_CustomVectorPushBack)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 79 | BENCHMARK(BM_StdVectorPushBack)->Range(1<<10, 1<<20)->Complexity()->Unit(benchmark::kMillisecond); 80 | BENCHMARK(BM_CustomVectorMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 81 | BENCHMARK(BM_StdVectorMiddleInsert)->Range(1<<10, 1<<15)->Complexity()->Unit(benchmark::kMillisecond); 82 | 83 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tasks/vector/vector/tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include "../vector.hpp" 2 | #include "../vector.cpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class Singleton { 16 | private: 17 | Singleton() {} 18 | 19 | public: 20 | Singleton(const Singleton&) = delete; 21 | Singleton& operator=(const Singleton&) = delete; 22 | 23 | static Singleton* getInstance() { 24 | if (instance == nullptr) { 25 | instance = new Singleton(); 26 | } 27 | return instance; 28 | } 29 | 30 | private: 31 | static Singleton* instance; 32 | }; 33 | 34 | Singleton* Singleton::instance = nullptr; 35 | 36 | 37 | class MemoryUseObject { 38 | public: 39 | MemoryUseObject() { 40 | a = malloc(100); 41 | }; 42 | 43 | MemoryUseObject(const MemoryUseObject&) { 44 | a = malloc(100); 45 | }; 46 | 47 | MemoryUseObject(MemoryUseObject&& other) { 48 | a = other.a; 49 | other.a = nullptr; 50 | }; 51 | 52 | MemoryUseObject& operator=(const MemoryUseObject&){ 53 | return *this; 54 | } 55 | 56 | MemoryUseObject& operator=(MemoryUseObject&& other){ 57 | if (a) { 58 | free(a); 59 | } 60 | a = other.a; 61 | other.a = nullptr; 62 | return *this; 63 | } 64 | 65 | ~MemoryUseObject(){ 66 | free(a); 67 | } 68 | 69 | 70 | private: 71 | void* a; 72 | }; 73 | 74 | 75 | struct President { 76 | std::string name; 77 | std::string country; 78 | int year; 79 | 80 | President(std::string p_name, std::string p_country, int p_year) 81 | : name(std::move(p_name)), country(std::move(p_country)), year(p_year) 82 | {} 83 | 84 | President(President&& other) 85 | : name(std::move(other.name)), country(std::move(other.country)), year(other.year) 86 | {} 87 | 88 | President& operator=(const President& other) = default; 89 | }; 90 | 91 | class VectorTest : public testing::Test { 92 | protected: 93 | void SetUp() override { 94 | vec.PushBack(1); 95 | vec.PushBack(2); 96 | vec.PushBack(3); 97 | vec.PushBack(4); 98 | vec.PushBack(5); 99 | vec.PushBack(6); 100 | vec.PushBack(7); 101 | assert(vec.Size() == sz); 102 | } 103 | 104 | Vector vec; 105 | const size_t sz = 7; 106 | }; 107 | 108 | TEST(EmptyVectorTest, DefaultConstructor) { 109 | Vector vec; 110 | ASSERT_TRUE(vec.IsEmpty()) << "Default vector isn't empty!"; 111 | ASSERT_EQ(vec.Capacity(), 0) << "Vector should not allocate memory in the default constructor!"; 112 | ASSERT_EQ(vec.Data(), nullptr) << "Vector should not allocate memory in the default constructor!"; 113 | } 114 | 115 | TEST(EmptyVectorTest, AssignIntConstructor) { 116 | Vector vec(10, 5); 117 | ASSERT_EQ(vec.Size(), 10); 118 | for (size_t i = 0; i < 10; ++i) { 119 | ASSERT_EQ(vec[i], 5); 120 | } 121 | } 122 | 123 | TEST(EmptyVectorTest, CopyConstructorWithPointers) { 124 | int a = 1; 125 | int b = 2; 126 | int c = 3; 127 | Vector vec1; 128 | vec1.PushBack(&a); 129 | vec1.PushBack(&b); 130 | vec1.PushBack(&c); 131 | Vector vec = vec1; 132 | ASSERT_NE(&vec1, &vec) << "Copy constructor must do copy!\n"; 133 | ASSERT_EQ(vec1.Size(), vec.Size()); 134 | for (size_t i = 0; i < vec.Size(); ++i) { 135 | ASSERT_EQ(*vec1[i], *vec[i]) << "Values must be equal!"; 136 | ASSERT_EQ(vec1[i], vec[i]) << "Need copy!"; 137 | } 138 | } 139 | 140 | 141 | TEST(EmptyVectorTest, CopyOperator) { 142 | Vector vec1; 143 | vec1.PushBack(MemoryUseObject()); 144 | Vector vec; 145 | vec1 = vec; 146 | ASSERT_NE(&vec1, &vec) << "Copy constructor must do copy!\n"; 147 | ASSERT_EQ(vec1.Size(), vec.Size()); 148 | } 149 | 150 | TEST(EmptyVectorTest, MoveOperator) { 151 | Vector vec1; 152 | vec1.PushBack(MemoryUseObject()); 153 | Vector vec; 154 | vec = std::move(vec1); 155 | ASSERT_EQ(vec.Size(), 1); 156 | ASSERT_EQ(vec1.Size(), 0); 157 | } 158 | 159 | TEST(EmptyVectorTest, Init_list) { 160 | Vector vec({1, 2, 3, 4, 5, 6, 7, 8, 9}); 161 | for (size_t i = 0; i < vec.Size(); ++i) { 162 | ASSERT_EQ(vec[i], i + 1); 163 | } 164 | } 165 | 166 | TEST(EmptyVectorTest, MoveToPushBack) { 167 | Vector> vec; 168 | std::unique_ptr ptr = std::make_unique(); 169 | vec.PushBack(std::move(ptr)); 170 | vec.PopBack(); // if work not correct will error with ASAN 171 | } 172 | 173 | TEST(EmptyVectorTest, JustReserve) { 174 | Vector vec; 175 | vec.Reserve(100); 176 | ASSERT_EQ(vec.Capacity(), 100); 177 | ASSERT_EQ(vec.Size(), 0); 178 | for (size_t i = 0; i < 99; ++i) { 179 | vec.PushBack(1); 180 | } 181 | ASSERT_EQ(vec.Capacity(), 100); 182 | ASSERT_EQ(vec.Size(), 99); 183 | } 184 | 185 | TEST(EmptyVectorTest, ReserveWithRealloc) { 186 | Vector vec({1, 2, 3, 4, 5, 6, 7, 8, 9}); 187 | vec.Reserve(100); 188 | ASSERT_EQ(vec.Capacity(), 100); 189 | ASSERT_EQ(vec.Size(), 9); 190 | for (size_t i = 0; i < vec.Size(); ++i) { 191 | ASSERT_EQ(vec[i], i + 1); 192 | } 193 | } 194 | 195 | TEST(EmptyVectorTest, ReserveWithNoEffect) { 196 | Vector vec({1, 2, 3, 4, 5, 6, 7, 8, 9}); 197 | vec.Reserve(1); 198 | ASSERT_EQ(vec.Capacity(), 10); 199 | ASSERT_EQ(vec.Size(), 9); 200 | for (size_t i = 0; i < vec.Size(); ++i) { 201 | ASSERT_EQ(vec[i], i + 1); 202 | } 203 | } 204 | 205 | TEST(EmptyVectorTest, OperatorSqueareBrackets) { 206 | Vector> vec; 207 | auto ptr = std::make_unique(); 208 | vec.PushBack(std::move(ptr)); 209 | 210 | std::thread t1([&](){ 211 | vec[0]->lock(); 212 | }); 213 | 214 | std::thread t2([&](){ 215 | vec.Front()->unlock(); 216 | }); 217 | 218 | std::thread t3([&](){ 219 | vec.Back()->lock(); 220 | }); 221 | 222 | t1.join(); 223 | t2.join(); 224 | 225 | auto future = std::async(std::launch::async, &std::thread::join, &t3); 226 | ASSERT_LT( 227 | future.wait_for(std::chrono::seconds(1)), 228 | std::future_status::timeout 229 | ) << "There is deadlock!\n"; 230 | } 231 | 232 | TEST(EmptyVectorTest, VectorEmplaceBack) { 233 | Vector vec; 234 | std::string name = "Nelson Mandela"; 235 | vec.EmplaceBack(name, "South Africa", 1994); 236 | ASSERT_FALSE(name.empty()); 237 | 238 | 239 | vec.EmplaceBack("Franklin Delano Roosevelt", "USA", 1936); 240 | 241 | ASSERT_EQ(vec.Size(), 2); 242 | ASSERT_EQ(vec[0].year, 1994); 243 | ASSERT_EQ(vec[1].year, 1936); 244 | } 245 | 246 | TEST(EmptyVectorTest, VoidAsTemplate) { 247 | Vector vec; 248 | vec.PushBack(malloc(1)); 249 | vec.PushBack(malloc(1)); 250 | vec.PushBack(malloc(1)); 251 | vec.PushBack(malloc(1)); 252 | vec.PushBack(malloc(1)); 253 | } 254 | 255 | 256 | TEST_F(VectorTest, CopyConstructor) { 257 | Vector vec1 = vec; 258 | ASSERT_NE(&vec1, &vec) << "Copy constructor must do copy!\n"; 259 | ASSERT_EQ(vec1.Size(), vec.Size()); 260 | for (size_t i = 0; i < vec.Size(); ++i) { 261 | ASSERT_EQ(vec1[i], vec[i]) << "Values must be equal!"; 262 | } 263 | } 264 | 265 | TEST_F(VectorTest, MoveConstructor) { 266 | Vector vec1 = std::move(vec); 267 | ASSERT_EQ(vec1.Size(), sz); 268 | for (size_t i = 0; i < vec.Size(); ++i) { 269 | ASSERT_EQ(vec1[i], i + 1); 270 | ASSERT_EQ(vec[i], 0); 271 | } 272 | } 273 | 274 | TEST_F(VectorTest, RawData) { 275 | auto data = vec.Data(); 276 | for (size_t i = 0; i < vec.Size(); ++i) { 277 | ASSERT_EQ(*(data + i), i + 1); 278 | } 279 | } 280 | 281 | 282 | TEST_F(VectorTest, VectorClear) { 283 | size_t old_cap = vec.Capacity(); 284 | vec.Clear(); 285 | ASSERT_EQ(vec.Capacity(), old_cap); 286 | ASSERT_EQ(vec.Size(), 0); 287 | } 288 | 289 | TEST_F(VectorTest, InsertFront) { 290 | vec.Insert(0, 0); 291 | ASSERT_EQ(vec.Size(), sz + 1); 292 | for (size_t i = 0; i < vec.Size(); ++i) { 293 | ASSERT_EQ(vec[i], i); 294 | } 295 | } 296 | 297 | TEST_F(VectorTest, InsertBack) { 298 | vec.Insert(sz, sz + 1); 299 | ASSERT_EQ(vec.Size(), sz + 1); 300 | for (size_t i = 0; i < vec.Size(); ++i) { 301 | ASSERT_EQ(vec[i], i + 1); 302 | } 303 | } 304 | 305 | TEST_F(VectorTest, InsertMid) { 306 | vec.Insert(sz / 2, 0); 307 | ASSERT_EQ(vec.Size(), sz + 1); 308 | for (size_t i = 0; i < vec.Size(); ++i) { 309 | if (i == sz / 2) { 310 | ASSERT_EQ(vec[i], 0); 311 | } else if (i < sz / 2) { 312 | ASSERT_EQ(vec[i], i + 1); 313 | } else { 314 | ASSERT_EQ(vec[i], i); 315 | } 316 | } 317 | } 318 | 319 | TEST_F(VectorTest, InsertWithResize) { 320 | size_t cur_cap = vec.Capacity(); 321 | for (size_t i = sz; i < cur_cap; ++i) { 322 | vec.PushBack(i + 1); 323 | } 324 | 325 | size_t pos = vec.Size() / 2; 326 | vec.Insert(pos, 0); 327 | ASSERT_NE(cur_cap, vec.Capacity()); 328 | for (size_t i = 0; i < vec.Size(); ++i) { 329 | if (i == vec.Size() / 2) { 330 | ASSERT_EQ(vec[i], 0); 331 | } else if (i < vec.Size() / 2) { 332 | ASSERT_EQ(vec[i], i + 1); 333 | } else { 334 | ASSERT_EQ(vec[i], i); 335 | } 336 | } 337 | } 338 | 339 | TEST_F(VectorTest, VectorPopBack) { 340 | vec.PopBack(); 341 | ASSERT_EQ(vec.Size(), sz - 1); 342 | for (size_t i = 0; i < vec.Size(); ++i) { 343 | ASSERT_EQ(vec[i], i + 1); 344 | } 345 | } 346 | 347 | TEST_F(VectorTest, VectorEraseAll) { 348 | size_t old_cap = vec.Capacity(); 349 | vec.Erase(0, sz); 350 | ASSERT_EQ(vec.Capacity(), old_cap); 351 | ASSERT_EQ(vec.Size(), 0); 352 | } 353 | 354 | TEST_F(VectorTest, VectorEraseFront) { 355 | size_t old_cap = vec.Capacity(); 356 | vec.Erase(0, 1); 357 | ASSERT_EQ(vec.Capacity(), old_cap); 358 | ASSERT_EQ(vec.Size(), sz - 1); 359 | for (size_t i = 0; i < vec.Size(); ++i) { 360 | ASSERT_EQ(vec[i], i + 2); 361 | } 362 | } 363 | 364 | TEST_F(VectorTest, VectorEraseBack) { 365 | size_t old_cap = vec.Capacity(); 366 | vec.Erase(sz - 1, sz); 367 | ASSERT_EQ(vec.Capacity(), old_cap); 368 | ASSERT_EQ(vec.Size(), sz - 1); 369 | for (size_t i = 0; i < vec.Size(); ++i) { 370 | ASSERT_EQ(vec[i], i + 1); 371 | } 372 | } 373 | 374 | TEST_F(VectorTest, VectorEraseMid) { 375 | size_t old_cap = vec.Capacity(); 376 | std::vector a = {1, 2, 5, 6, 7}; 377 | vec.Erase(sz / 2 - 1, sz / 2 + 1); // 2 - 4 378 | ASSERT_EQ(vec.Capacity(), old_cap); 379 | ASSERT_EQ(vec.Size(), sz - 2); 380 | for (size_t i = 0; i < vec.Size(); ++i) { 381 | ASSERT_EQ(vec[i], a[i]); 382 | } 383 | } 384 | 385 | TEST_F(VectorTest, VectorEraseNoneExistingPositions) { 386 | size_t old_cap = vec.Capacity(); 387 | vec.Erase(sz + 1, sz + 3); // no effect 388 | ASSERT_EQ(vec.Capacity(), old_cap); 389 | ASSERT_EQ(vec.Size(), sz); 390 | for (size_t i = 0; i < vec.Size(); ++i) { 391 | ASSERT_EQ(vec[i], i + 1); 392 | } 393 | } 394 | 395 | TEST_F(VectorTest, VectorResizeGreaterThenCurrent) { 396 | size_t old_cap = vec.Capacity(); 397 | size_t old_size = vec.Size(); 398 | vec.Resize(old_size + old_cap, 0); 399 | ASSERT_NE(vec.Capacity(), old_cap); 400 | ASSERT_EQ(vec.Size(), old_size + old_cap); 401 | for (size_t i = 0; i < old_size; ++i) { 402 | ASSERT_EQ(vec[i], i + 1); 403 | } 404 | for (size_t i = old_size; i < vec.Size(); ++i) { 405 | ASSERT_EQ(vec[i], 0); 406 | } 407 | } 408 | 409 | TEST_F(VectorTest, VectorResizeEqualCurrent) { 410 | size_t old_cap = vec.Capacity(); 411 | size_t old_size = vec.Size(); 412 | vec.Resize(old_size, 0); // no effect 413 | ASSERT_EQ(vec.Capacity(), old_cap); 414 | ASSERT_EQ(vec.Size(), old_size); 415 | for (size_t i = 0; i < old_size; ++i) { 416 | ASSERT_EQ(vec[i], i + 1); 417 | } 418 | } 419 | 420 | TEST_F(VectorTest, VectorResizeLessThenCurrent) { 421 | size_t old_cap = vec.Capacity(); 422 | size_t old_size = vec.Size(); 423 | vec.Resize(old_size - 4, 0); // reducing 424 | ASSERT_EQ(vec.Capacity(), old_cap); 425 | ASSERT_EQ(vec.Size(), old_size - 4); 426 | for (size_t i = 0; i < old_size - 4; ++i) { 427 | ASSERT_EQ(vec[i], i + 1); 428 | } 429 | } 430 | 431 | 432 | int main(int argc, char** argv) { 433 | ::testing::InitGoogleTest(&argc, argv); 434 | 435 | return RUN_ALL_TESTS(); 436 | } -------------------------------------------------------------------------------- /tasks/vector/vector/vector.cpp: -------------------------------------------------------------------------------- 1 | #include "vector.hpp" 2 | 3 | template 4 | Vector::Vector() { 5 | // Not Implemented 6 | } 7 | 8 | template 9 | Vector::Vector(size_t /*count*/, const T& /*value*/) { 10 | // Not Implemented 11 | } 12 | 13 | template 14 | Vector::Vector(const Vector& /*other*/) { 15 | // Not Implemented 16 | } 17 | 18 | template 19 | Vector& Vector::operator=(const Vector& /*other*/) { 20 | std::abort(); // Not Implemented 21 | } 22 | 23 | template 24 | Vector& Vector::operator=(Vector&& /*other*/) { 25 | std::abort(); // Not Implemented 26 | } 27 | 28 | template 29 | Vector::Vector(Vector&& /*other*/) noexcept { 30 | // Not Implemented 31 | } 32 | 33 | template 34 | Vector::Vector(std::initializer_list /*init*/) { 35 | // Not Implemented 36 | } 37 | 38 | template 39 | T& Vector::operator[](size_t /*pos*/) { 40 | std::abort(); // Not Implemented 41 | } 42 | 43 | template 44 | T& Vector::Front() const noexcept { 45 | std::abort(); // Not Implemented 46 | } 47 | 48 | template 49 | bool Vector::IsEmpty() const noexcept { 50 | std::abort(); // Not Implemented 51 | } 52 | 53 | template 54 | T& Vector::Back() const noexcept { 55 | std::abort(); // Not Implemented 56 | } 57 | 58 | template 59 | T* Vector::Data() const noexcept { 60 | std::abort(); // Not Implemented 61 | } 62 | 63 | template 64 | size_t Vector::Size() const noexcept { 65 | std::abort(); // Not Implemented 66 | } 67 | 68 | template 69 | size_t Vector::Capacity() const noexcept { 70 | std::abort(); // Not Implemented 71 | } 72 | 73 | template 74 | void Vector::Reserve(size_t /*new_cap*/) { 75 | // Not Implemented 76 | } 77 | 78 | template 79 | void Vector::Clear() noexcept { 80 | // Not Implemented 81 | } 82 | 83 | template 84 | void Vector::Insert(size_t /*pos*/, T /*value*/) { 85 | // Not Implemented 86 | } 87 | 88 | template 89 | void Vector::Erase(size_t /*begin_pos*/, size_t /*end_pos*/) { 90 | // Not Implemented 91 | } 92 | 93 | template 94 | void Vector::PushBack(T /*value*/) { 95 | // Not Implemented 96 | } 97 | 98 | template 99 | template 100 | void Vector::EmplaceBack(Args&&... /*args*/) { 101 | // Not Implemented 102 | } 103 | 104 | template 105 | void Vector::PopBack() { 106 | // Not Implemented 107 | } 108 | 109 | template 110 | void Vector::Resize(size_t /*count*/, const T& /*value*/) { 111 | // Not Implemented 112 | } 113 | 114 | template 115 | Vector::~Vector() { 116 | // Not Implemented 117 | } -------------------------------------------------------------------------------- /tasks/vector/vector/vector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | class Vector { 8 | public: 9 | Vector(); 10 | 11 | Vector(size_t count, const T& value); 12 | 13 | Vector(const Vector& other); 14 | 15 | Vector& operator=(const Vector& other); 16 | 17 | Vector(Vector&& other) noexcept; 18 | 19 | Vector& operator=(Vector&& other); 20 | 21 | Vector(std::initializer_list init); 22 | 23 | T& operator[](size_t pos); 24 | 25 | T& Front() const noexcept; 26 | 27 | T& Back() const noexcept; 28 | 29 | T* Data() const noexcept; 30 | 31 | bool IsEmpty() const noexcept; 32 | 33 | size_t Size() const noexcept; 34 | 35 | size_t Capacity() const noexcept; 36 | 37 | void Reserve(size_t new_cap); 38 | 39 | void Clear() noexcept; 40 | 41 | void Insert(size_t pos, T value); 42 | 43 | void Erase(size_t begin_pos, size_t end_pos); 44 | 45 | void PushBack(T value); 46 | 47 | template 48 | void EmplaceBack(Args&&... args); 49 | 50 | void PopBack(); 51 | 52 | void Resize(size_t count, const T& value); 53 | 54 | ~Vector(); 55 | 56 | private: 57 | /*???*/ 58 | }; 59 | --------------------------------------------------------------------------------