diff --git a/.gitignore b/.gitignore
index 87e93c3aa6db1c5ccac4ce33203d05067441716e..1a7d54eac0d19c1b43d57bb7296c95b1e8aefadb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-*.o
-build
-obj
+/build
+/out
 *.html
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 08a8c0e2903f8a12be4714a1732a588cb0f3f376..01520f4d071a2217871ad199fe411247f42e699c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,5 +13,8 @@ linter:
 test:
   stage: test 
   script:
-    - make -sj SANITIZER=all
-    - make -sk test SANITIZER=all
+    - cmake -B ./build/ -G "Ninja Multi-Config" -DCMAKE_C_COMPILER=clang
+    - cmake --build ./build/ --config ASan  --target check
+    - cmake --build ./build/ --config LSan  --target check
+    - cmake --build ./build/ --config MSan  --target check
+    - cmake --build ./build/ --config UBSan --target check
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2330220dc7bc2351338c8296709f3853f61fdb37
--- /dev/null
+++ b/.idea/cmake.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CMakeSharedSettings">
+    <configurations>
+      <configuration PROFILE_NAME="Debug" ENABLED="true" GENERATION_DIR="out/build/debug" CONFIG_NAME="Debug" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=Debug" />
+      <configuration PROFILE_NAME="Release" ENABLED="true" GENERATION_DIR="out/build/release" CONFIG_NAME="Release" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=Release" />
+      <configuration PROFILE_NAME="ASan" ENABLED="true" GENERATION_DIR="out/build/asan" CONFIG_NAME="ASan" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=ASan" />
+      <configuration PROFILE_NAME="LSan" ENABLED="true" GENERATION_DIR="out/build/lsan" CONFIG_NAME="LSan" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=LSan" />
+      <configuration PROFILE_NAME="MSan" ENABLED="true" GENERATION_DIR="out/build/msan" CONFIG_NAME="MSan" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=MSan" />
+      <configuration PROFILE_NAME="UBSan" ENABLED="true" GENERATION_DIR="out/build/ubsan" CONFIG_NAME="UBSan" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=UBSan" />
+    </configurations>
+  </component>
+</project>
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e5c26d2f0239822ba05884f4ba45d4fa19752549
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,54 @@
+cmake_minimum_required(VERSION 3.12)
+
+project(image-transformer LANGUAGES C)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+if(CMAKE_C_COMPILER_ID STREQUAL GNU)
+    set(CMAKE_CONFIGURATION_TYPES Debug Release ASan LSan UBSan)
+elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
+    set(CMAKE_CONFIGURATION_TYPES Debug Release ASan LSan MSan UBSan)
+elseif(MSVC)
+    set(CMAKE_CONFIGURATION_TYPES Debug Release ASan)
+endif()
+
+if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST CMAKE_CONFIGURATION_TYPES)
+    message(FATAL_ERROR "Unexpected build type ${CMAKE_BUILD_TYPE}, possible values: ${CMAKE_CONFIGURATION_TYPES}")
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID MATCHES Clang)
+    set(CMAKE_C_FLAGS       "-std=c17 -pedantic -Wall -Werror -ggdb")
+    set(CMAKE_C_FLAGS_ASAN  "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-optimize-sibling-calls -fno-omit-frame-pointer")
+    set(CMAKE_C_FLAGS_LSAN  "${CMAKE_C_FLAGS_DEBUG} -fsanitize=leak")
+    set(CMAKE_C_FLAGS_MSAN  "${CMAKE_C_FLAGS_DEBUG} -fsanitize=memory -fno-optimize-sibling-calls -fno-omit-frame-pointer")
+    set(CMAKE_C_FLAGS_UBSAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined")
+elseif(MSVC)
+    set(CMAKE_C_FLAGS       "/std:c17 /W4 /WX")
+    set(CMAKE_C_FLAGS_ASAN  "${CMAKE_C_FLAGS_DEBUG} /fsanitize=address")
+    set(CMAKE_EXE_LINKER_FLAGS_ASAN "/debug /INCREMENTAL:NO")
+endif()
+
+if(WIN32)
+    add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
+endif()
+
+find_program(CLANG_TIDY clang-tidy)
+message(STATUS "Clang-tidy: ${CLANG_TIDY}")
+if(CLANG_TIDY)
+    file(STRINGS clang-tidy-checks.txt clang_tidy_checks)
+    list(JOIN clang_tidy_checks "," clang_tidy_checks_str)
+    set(CMAKE_C_CLANG_TIDY
+        ${CLANG_TIDY}
+        -header-filter=${CMAKE_SOURCE_DIR}
+        -checks=${clang_tidy_checks_str}
+        -warnings-as-errors=*
+    )
+endif()
+
+add_subdirectory(solution)
+
+option(BUILD_TESTING "Enable tests" ON)
+if(BUILD_TESTING)
+    enable_testing()
+    add_subdirectory(tester)
+endif()
diff --git a/CMakeSettings.json b/CMakeSettings.json
new file mode 100644
index 0000000000000000000000000000000000000000..f7aa3175439b9950fc37fb9dc8f79847249ed23a
--- /dev/null
+++ b/CMakeSettings.json
@@ -0,0 +1,37 @@
+п»ї{
+  "configurations": [
+    {
+      "name": "x64-Debug",
+      "generator": "Ninja",
+      "configurationType": "Debug",
+      "inheritEnvironments": [ "msvc_x64_x64" ],
+      "buildRoot": "${projectDir}\\out\\build\\${name}",
+      "installRoot": "${projectDir}\\out\\install\\${name}",
+      "cmakeCommandArgs": "",
+      "buildCommandArgs": "",
+      "ctestCommandArgs": ""
+    },
+    {
+      "name": "x64-Release",
+      "generator": "Ninja",
+      "configurationType": "Release",
+      "inheritEnvironments": [ "msvc_x64_x64" ],
+      "buildRoot": "${projectDir}\\out\\build\\${name}",
+      "installRoot": "${projectDir}\\out\\install\\${name}",
+      "cmakeCommandArgs": "",
+      "buildCommandArgs": "",
+      "ctestCommandArgs": ""
+    },
+    {
+      "name": "x64-Asan",
+      "generator": "Ninja",
+      "configurationType": "ASan",
+      "inheritEnvironments": [ "msvc_x64_x64" ],
+      "buildRoot": "${projectDir}\\out\\build\\${name}",
+      "installRoot": "${projectDir}\\out\\install\\${name}",
+      "cmakeCommandArgs": "",
+      "buildCommandArgs": "",
+      "ctestCommandArgs": ""
+    }
+  ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index d3b00991a09b744ce075398e4b46c37cee21a0bf..c5b3000febf8cfd8cb2bf213e19828ad9b3f2c2b 100644
--- a/README.md
+++ b/README.md
@@ -229,46 +229,55 @@ bmp-заголовка.
 
 ## Система сборки и тестирования
 
-- Используйте команду `make` для того, чтобы собрать ваш код в исполняемый файл
-  `build/image-transformer`. Проверьте, что в вашей системе установлены программа
-  `make` версии как минимум 4.0 и компилятор `clang`. Флаги для компиляции берутся
-  из `compile_flags.txt` &mdash; вы можете добавить свои флаги в конец
-  этого файла или в саму команду `make`. Например, чтобы собрать код с оптимизациями
-  и протестировать скорость выполнения, вы можете использовать `make CFLAGS=-O3`.
-  С помощью `LDFLAGS` можно передать дополнительные параметры линковщику.
-- Используйте `make check`, чтобы проверить вашу программу через статический анализатор
-  `clang-tidy`. Список проверок описан в файле `clang-tidy-checks.txt`. Вы можете добавить
-  свои проверки в конец этого файла или в параметр `CLANG_TIDY_CHECKS` для `make`.
-- Вы можете собрать код с поддержкой определенных динамических анализаторов (санитайзеров).
-  Санитайзеры могут дать подробную информацию о возможных и реальных ошибках в программе вместо
-  классического сообщения о segmentation fault. Исполняемые файлы будут также размещены в директории `build`, но для санитайзеров выделены отдельные
-  поддиректории с их названием.
-  Поддерживаются следующие санитайзеры:
-  - `make SANITIZER=asan` &mdash; [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html),
+Для сборки кода вам предоставлена система сборки на языке CMake, самим писать систему сборки не требуется.
+
+- В зависимости от платформы и компилятора, система сборки поддерживает несколько конфигураций с динамическими
+  анализаторами (санитайзерами). Санитайзеры могут дать подробную информацию о возможных и реальных ошибках в
+  программе вместо классического сообщения о segmentation fault. Выбрать подходящую конфигурацию вы можете с
+  помощью переменной `CMAKE_BUILD_TYPE`:
+
+  - `ASan` &mdash; [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html),
     набор проверок на некорректное использование адресов памяти. Примеры:
     use-after-free, double-free, выход за пределы стека, кучи или статического блока.
-  - `make SANITIZER=lsan` &mdash; [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html),
+
+  - `LSan` &mdash; [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html),
     проверки на утечки памяти.
-  - `make SANITIZER=msan` &mdash; [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html),
+
+  - `MSan` &mdash; [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html),
     проверяет, что любая используемая ячейка памяти проинициализирована на момент чтения из нее.
-  - `make SANITIZER=usan` &mdash; [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html),
+
+  - `UBSan` &mdash; [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html),
     набор базовых проверок на неопределенное поведение. Примеры: переполнение численного типа,
     null-pointer dereference.
-  - `make SANITIZER=none` &mdash; собрать код без санитайзеров (по умолчанию).
-  - `make SANITIZER=all` &mdash; собрать 5 версий кода, каждая с одной из предыдущих опций.
-- Используйте `make clean` для очистки временных файлов и директории `build`.
-- Директория `tester` содержит код и изображения для тестирования вашей программы. Используйте
-  `make -k test`, чтобы запустить тесты и вывести отчет по их выполнению. Параметры `CFLAGS`,
-  `LDFLAGS` и `SANITIZER` также могут быть переданы программам, используемым в тестах.
-- Чтобы запустить конкретный тест, посмотрите его название в отчете всех тестов (оно же название
-  директории с изображениями в `testers/tests`) и запустите `make -k test-<name>`. Например,
-  чтобы запустить тест №1 ([изначальная картинка](testers/tests/1/input.bmp) ->
-  [ожидаемый результат](testers/tests/1/output_expected.bmp)), используйте `make -k test-1`.
-- Ваша программа будет тестироваться в
-  [Docker-контейнере](https://gitlab.se.ifmo.ru/low-level-programming/docker-c-test-machine/),
-  вы можете использовать его для тестирования вашего кода локально. В контейнере будут запущены:
-  - Статический анализатор - аналогичен команде `make check`
-  - Тесты с санитайзерами - аналогичен команде `make -k test SANITIZER=all`
+
+- Если в вашей системе имеется статический анализатор `clang-tidy`, он будет запущен во время компиляции программы.
+  Список проверок описан в файле `clang-tidy-checks.txt`. Вы можете добавить свои проверки в конец этого файла.
+
+- Директория `tester` содержит код и изображения для тестирования вашей программы. Для запуска тестов используется CTest.
+
+- Поддержана интеграция системы сборки со средами разработки CLion, Visual Studio и Visual Studio Code.
+
+Чтобы система сборки работала на вашей системе, вам необходимо:
+
+### Linux Рё MacOS
+
+- Компилятор (`gcc`/`clang`) и `cmake` (проверьте, что `cmake` версии 3.12 или выше)
+- Если вы хотите использовать санитайзеры с GCC, установите `libasan`, `liblsan` и `libubsan` с помощью пакетного менеджера (названия могут отличаться).
+- Если вы хотите использовать санитайзеры с Clang, на некоторых системах вам может понадобиться пакет `compiler-rt`.
+- Если вы хотите пользоваться `clang-tidy`, установите `clang-tools-extra`.
+
+### Windows
+
+- Какая-либо среда разработки (CLion, Visual Studio, Visual Studio Code)
+- Если вы хотите пользоваться `clang-tidy`, скачайте LLVM: https://github.com/llvm/llvm-project/releases (найдите установщик win64 под одной из версий)
+- Для VS Code требуется отдельно поставить Visual Studio (с сайта Microsoft) и CMake: https://cmake.org/download/
+
+### Инструкции по сборке и тестированию
+
+- [Работа с терминалом](docs/Terminal.md)
+- [CLion](docs/CLion.md)
+- [Visual Studio](docs/Visual%20Studio.md)
+- [Visual Studio Code](docs/VSCode.md)
 
 # Для самопроверки
 
diff --git a/cmake-variants.yaml b/cmake-variants.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1986067bea31b509649a1821f36890674328b53b
--- /dev/null
+++ b/cmake-variants.yaml
@@ -0,0 +1,28 @@
+buildType:
+  default: debug
+  description: Build Type
+  choices:
+    debug:
+      short: Debug
+      long: Build with debugging information
+      buildType: Debug
+    release:
+      short: Release
+      long: Optimize the resulting binaries
+      buildType: Release
+    asan:
+      short: ASan
+      long: Instrument with AddressSanitizer
+      buildType: ASan
+    lsan:
+      short: LSan
+      long: Instrument with LeakSanitizer
+      buildType: LSan
+    msan:
+      short: MSan
+      long: Instrument with MemorySanitizer
+      buildType: MSan
+    ubsan:
+      short: UBSan
+      long: Instrument with UndefinedBehaviourSanitizer
+      buildType: UBSan
diff --git a/docs/CLion.md b/docs/CLion.md
new file mode 100644
index 0000000000000000000000000000000000000000..c7be3f0d0edd0087559ed7ffab779c9611fea25c
--- /dev/null
+++ b/docs/CLion.md
@@ -0,0 +1,30 @@
+# Разработка с CLion
+
+## 1. Выберите и склонируйте ваш форк с GitLab
+
+![Select project from VCS](CLion/01-get-from-vcs.png)
+
+## 2. В окне проекта, выберите `CMakeLists.txt` на панели слева
+
+![Load CMake project](CLion/02-load-cmake-project.png)
+
+Сверху окна с редактором появится подсказка, предлагающая загрузить CMake в проект. Нажмите **`Load CMake project`**.
+
+В окне сборки может появиться ошибка, например: `Unexpected build type MSan, possible values: Debug;Release;ASan;LSan;UBSan`.
+Она означает, что данной конфигурации на вашей ОС или с вашим компилятором не предусмотрено,
+но разработке с другими конфигурациями она не помешает.
+
+## 3. Выберите необходимую конфигурацию в раскрывающемся списке
+
+![Choose Config](CLion/03-choose-configuration.png)
+
+- **`Debug`** быстро компилируется и подходит для разработки.
+- **`ASan, LSan, MSan, UBSan`** подходят для отладки ошибок сегментации и других проблем с памятью. Рекомендуется 
+  запустить ваш код с санитайзерами перед отправкой на проверку!
+- **`Release`** нужен для сборки кода с оптимизациями и проверки скорости выполнения.
+
+В качестве цели для сборки выберите **`All CTest`**. Теперь вы можете собирать проект и
+запускать его через кнопки сверху справа как обычно.
+
+Если при сборке вы получили ошибку вроде `C:\CLion 2022.2.4\bin\mingw\bin/ld.exe: cannot find -lasan`, значит, у вас
+нет нужной библиотеки для запуска данного профиля. Можете просто выбрать другую конфигурацию.
diff --git a/docs/CLion/01-get-from-vcs.png b/docs/CLion/01-get-from-vcs.png
new file mode 100644
index 0000000000000000000000000000000000000000..1cc5866b4c60fbcc13f1d10f26fb054312231898
Binary files /dev/null and b/docs/CLion/01-get-from-vcs.png differ
diff --git a/docs/CLion/02-load-cmake-project.png b/docs/CLion/02-load-cmake-project.png
new file mode 100644
index 0000000000000000000000000000000000000000..e548687bdf02b1873d0deeb1bc524fa3d3632035
Binary files /dev/null and b/docs/CLion/02-load-cmake-project.png differ
diff --git a/docs/CLion/03-choose-configuration.png b/docs/CLion/03-choose-configuration.png
new file mode 100644
index 0000000000000000000000000000000000000000..3dc34a7cb8aa1c1dabc8f8f01ae3c4fe22c2e8d6
Binary files /dev/null and b/docs/CLion/03-choose-configuration.png differ
diff --git a/docs/Terminal.md b/docs/Terminal.md
new file mode 100644
index 0000000000000000000000000000000000000000..8992affd8dc2c98f3c010c550e33c2d2ef2e2879
--- /dev/null
+++ b/docs/Terminal.md
@@ -0,0 +1,75 @@
+# Работа с терминалом
+
+## 1. Конфигурация
+
+```bash
+$ mkdir ./build/
+$ cd ./build/
+$ cmake .. -DCMAKE_BUILD_TYPE=<config>
+```
+
+Вместо `<config>` можно использовать одну из существующих конфигураций:
+
+- **`Debug`** быстро компилируется и подходит для разработки.
+- **`ASan, LSan, MSan, UBSan`** подходят для отладки ошибок сегментации и других проблем с памятью. Рекомендуется 
+  запустить ваш код с санитайзерами перед отправкой на проверку!
+- **`Release`** нужен для сборки кода с оптимизациями и проверки скорости выполнения.
+
+## 2. РЎР±РѕСЂРєР°
+
+```bash
+$ cmake --build ./build/
+```
+
+Исполняемые файлы `./build/solution/image-transformer` и `./build/tester/image-matcher`
+будут собраны, их можно использовать для ручного тестирования.
+
+## 3. Тестирование
+
+```bash
+$ cmake --build ./build/ --target check
+# ИЛИ
+$ cd ./build/
+$ ctest --output-on-failure
+```
+
+## Бонус: `ssh` + `git`
+
+### Как настроить SSH ключи
+
+```bash
+$ ssh-keygen
+$ cat ~/.ssh/id_rsa.pub
+```
+
+В настройках профиля GitLab нужно открыть категорию `SSH Keys`, добавить новый
+ключ и скопировать содержимое `id_rsa.pub` туда.
+
+### Как склонировать форк
+
+```bash
+$ git clone ssh://git@gitlab.se.ifmo.ru:4815/<my username>/assignment-image-rotation.git
+$ cd ./assignment-image-rotation/
+```
+
+### Как отправить свои изменения обратно в форк
+
+```bash
+$ git add ./solution/
+$ git status
+$ git commit -m "Lab complete"
+$ git push origin master
+```
+
+После того, как вы откроете merge request, каждое новое изменение, добавленное таким образом,
+будет появляться там автомагически.
+
+### Как обновить свою лабораторную, если преподаватель попросил "подтянуть к себе свежие изменения" из основного репозитория
+
+```bash
+$ git remote add upstream ssh://git@gitlab.se.ifmo.ru:4815/programming-languages/assignment-image-rotation.git
+$ git fetch upstream
+$ git checkout master
+$ git merge upstream/master
+$ git remote remove upstream
+```
diff --git a/docs/VS/01-vcs.png b/docs/VS/01-vcs.png
new file mode 100644
index 0000000000000000000000000000000000000000..19344a17ee044ba33000f900201f023dd1e47b87
Binary files /dev/null and b/docs/VS/01-vcs.png differ
diff --git a/docs/VS/02-folder-view.png b/docs/VS/02-folder-view.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c57e63995ac6e1570eb4c49222a6c9e43c4d435
Binary files /dev/null and b/docs/VS/02-folder-view.png differ
diff --git a/docs/VS/03-choose-config.png b/docs/VS/03-choose-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d3f0a76aedfb7eb1e5444324234ff5cce7f8477
Binary files /dev/null and b/docs/VS/03-choose-config.png differ
diff --git a/docs/VS/04-tests.png b/docs/VS/04-tests.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d43ef3e1bf9652e79c47d4289ddb74d6b83c40b
Binary files /dev/null and b/docs/VS/04-tests.png differ
diff --git a/docs/VSCode.md b/docs/VSCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..d2f67a318dbd716457a5774f8ab03b7b0d16610a
--- /dev/null
+++ b/docs/VSCode.md
@@ -0,0 +1,35 @@
+# Разработка с Visual Studio Code
+
+Для разработки требуются расширения:
+
+- **`CMake`** РѕС‚ `twxs` 
+- **`CMakeTools`** РѕС‚ `Microsoft` 
+- **`C/C++`** РѕС‚ `Microsoft` 
+
+Перезагрузите VSCode после установки расширений.
+
+## 1. Выберите и склонируйте ваш форк с GitLab
+
+![Clone repository](VSCode/01-clone.png)
+
+## 2. В окне проекта, выберите компилятор
+
+![Choose Kit](VSCode/02-choose-kit.png)
+
+VSCode предложит выбрать компилятор при открытии проекта. Если он этого не сделал,
+кликните по кнопке с гаечным ключом на нижней панели.
+
+На Windows, вероятнее всего, вам нужен компилятор `Visual Studio` с версией `amd64`.
+
+## 3. Выберите конфигурацию на нижней панели
+
+![Choose Config](VSCode/03-choose-config.png)
+
+- **`Debug`** быстро компилируется и подходит для разработки.
+- **`ASan, LSan, MSan, UBSan`** подходят для отладки ошибок сегментации и других проблем с памятью. Рекомендуется 
+  запустить ваш код с санитайзерами перед отправкой на проверку!
+- **`Release`** нужен для сборки кода с оптимизациями и проверки скорости выполнения.
+
+На той же нижней панели, используйте кнопку **`Build`** для сборки кода и **`Run CTests tests`** для запуска тестов.
+
+Если во время сборки вы видите ошибку вроде `...\Microsoft.CppBuild.targets(457,5): error MSB8013: This project doesn't contain the Configuration and Platform combination of MSan|x64`, это означает что выбранная конфигурация на вашей системе не поддержана - выберите другую.
diff --git a/docs/VSCode/01-clone.png b/docs/VSCode/01-clone.png
new file mode 100644
index 0000000000000000000000000000000000000000..b14f8a2e016f28abc07beb4ce4eb34eca2850383
Binary files /dev/null and b/docs/VSCode/01-clone.png differ
diff --git a/docs/VSCode/02-choose-kit.png b/docs/VSCode/02-choose-kit.png
new file mode 100644
index 0000000000000000000000000000000000000000..cdc325ba3a7c1e108b3f5d7011cc7f2a5498312a
Binary files /dev/null and b/docs/VSCode/02-choose-kit.png differ
diff --git a/docs/VSCode/03-choose-config.png b/docs/VSCode/03-choose-config.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5a15983515a46da9813c70da7a210be06d73a85
Binary files /dev/null and b/docs/VSCode/03-choose-config.png differ
diff --git a/docs/Visual Studio.md b/docs/Visual Studio.md
new file mode 100644
index 0000000000000000000000000000000000000000..59c0e152abafd5b3245ee1d93d74838b2b75763d
--- /dev/null
+++ b/docs/Visual Studio.md	
@@ -0,0 +1,27 @@
+# Разработка с Visual Studio
+
+## 1. Выберите и склонируйте ваш форк с GitLab
+
+![Select project from VCS](VS/01-vcs.png)
+
+## 2. В окне решения, выберите `Folder View` на панели слева
+
+![Select Folder View](VS/02-folder-view.png)
+
+Visual Studio запустит конфигурацию проекта с помощью CMake в профиле по умолчанию (**`x64-Debug`**).
+
+## 3. Выберите необходимую конфигурацию в раскрывающемся списке
+
+![Choose Config](VS/03-choose-config.png)
+
+- **`x64-Debug`** быстро компилируется и подходит для разработки.
+- **`x64-Asan`** подходит для отладки ошибок сегментации и других проблем с памятью. Рекомендуется 
+  запустить ваш код в Asan перед отправкой на проверку!
+- **`x64-Release`** нужен для сборки кода с оптимизациями и проверки скорости выполнения.
+
+## 4. Используйте панели сверху для запуска сборки и тестирования
+
+![Run build and tests](VS/04-tests.png)
+
+- Варианты для сборки находятся в меню **`Build`**. Для сборки всего решения нажмите **`F7`**.
+- Для запуска тестов выберите **`Run CTests for ...`** в меню **`Test`**.
diff --git a/solution/CMakeLists.txt b/solution/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b8ff60995419877a10093ed0377e4951e1fa0981
--- /dev/null
+++ b/solution/CMakeLists.txt
@@ -0,0 +1,8 @@
+file(GLOB_RECURSE sources CONFIGURE_DEPENDS
+    src/*.c
+    src/*.h
+    include/*.h
+)
+
+add_executable(image-transformer ${sources})
+target_include_directories(image-transformer PRIVATE src include)
diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b702752aca2a943078cf41efe08c297a33543bf2
--- /dev/null
+++ b/tester/CMakeLists.txt
@@ -0,0 +1,28 @@
+file(GLOB_RECURSE sources CONFIGURE_DEPENDS
+    src/*.c
+    src/*.h
+    include/*.h
+)
+
+add_executable(image-matcher ${sources})
+target_include_directories(image-matcher PRIVATE src include)
+
+file(GLOB test_directories CONFIGURE_DEPENDS tests/*)
+list(FILTER test_directories EXCLUDE REGEX ".*/\.gitignore")
+
+foreach(test_dir IN LISTS test_directories)
+    string(REPLACE "/" ";" name_components ${test_dir})
+    list(GET name_components -1 name)
+    add_test(NAME test-${name}
+        COMMAND ${CMAKE_COMMAND}
+            -DTEST_DIR=${test_dir}
+            -DIMAGE_TRANSFORMER=$<TARGET_FILE:image-transformer>
+            -DIMAGE_MATCHER=$<TARGET_FILE:image-matcher>
+            -P ${CMAKE_CURRENT_SOURCE_DIR}/tester.cmake
+    )
+endforeach()
+
+set(CMAKE_CTEST_ARGUMENTS --output-on-failure -C $<CONFIG>)
+add_custom_target(check
+    COMMAND ${CMAKE_CTEST_COMMAND} ${CMAKE_CTEST_ARGUMENTS}
+    DEPENDS image-transformer image-matcher)
diff --git a/tester/include/image.h b/tester/include/image.h
index efced9fecf3d04b99bd4fa54c2b0648071bfd33f..da8021d53ac06686be3193a2ba631530ea21e07b 100644
--- a/tester/include/image.h
+++ b/tester/include/image.h
@@ -1,8 +1,8 @@
 #pragma once
 
-#include <stddef.h>
-#include <malloc.h>
 #include <inttypes.h>
+#include <malloc.h>
+#include <stddef.h>
 
 #include "dimensions.h"
 
diff --git a/tester/src/bmp.c b/tester/src/bmp.c
index 4554ac22829ffd9cf3a75e8e246bafb825eee723..4f5926e548b32e70de41ef669ef8e3a331a9ab75 100644
--- a/tester/src/bmp.c
+++ b/tester/src/bmp.c
@@ -27,9 +27,11 @@
 
 #define DECLARE_FIELD(t, n) t n;
 
-struct __attribute__((packed)) header {
+#pragma pack(push, 1)
+struct header {
   FOR_HEADER(DECLARE_FIELD)
 };
+#pragma pack(pop)
 
 #undef FOR_HEADER
 #undef DECLARE_FIELD
@@ -66,8 +68,8 @@ enum bmp_compare_status bmp_cmp(FILE *f1, FILE *f2) {
     case CMP_DIFF: return BMP_CMP_DIFF;
     case CMP_ERROR: return BMP_CMP_FILE_ERROR;
     case CMP_EQ:
-      if (fseek(f1, padding, SEEK_CUR) != 0 ||
-          fseek(f2, padding, SEEK_CUR) != 0)
+      if (fseek(f1, (long) padding, SEEK_CUR) != 0 ||
+          fseek(f2, (long) padding, SEEK_CUR) != 0)
         return BMP_CMP_FILE_ERROR;
       break;
     default: err("Implementation error"); break;
diff --git a/tester/tester.cmake b/tester/tester.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..0dcc9196f12228d99571bd0b4a7dd801b7f00414
--- /dev/null
+++ b/tester/tester.cmake
@@ -0,0 +1,25 @@
+# CMP0007: list command no longer ignores empty elements.
+if(POLICY CMP0007)
+    cmake_policy(SET CMP0007 NEW)
+endif()
+
+function(exec_check)
+    execute_process(COMMAND ${ARGV}
+        OUTPUT_VARIABLE out
+        ERROR_VARIABLE  err
+        RESULT_VARIABLE result)
+    if(result)
+        string(REPLACE "/" ";" name_components ${ARGV0})
+        list(GET name_components -1 name)
+        if(NOT out)
+            set(out "<empty>")
+        endif()
+        if(NOT err)
+            set(err "<empty>")
+        endif()
+        message(FATAL_ERROR "\nError running \"${name}\"\n*** Output: ***\n${out}\n*** Error: ***\n${err}\n")
+    endif()
+endfunction()
+
+exec_check(${IMAGE_TRANSFORMER} ${TEST_DIR}/input.bmp ${TEST_DIR}/output.bmp)
+exec_check(${IMAGE_MATCHER} ${TEST_DIR}/output.bmp ${TEST_DIR}/output_expected.bmp)
diff --git a/tester/tester.sh b/tester/tester.sh
index 06e166a29054b1476011feb2099d50c816751d30..bafd9910b681cdc7c96c5508d92c72165253c09c 100755
--- a/tester/tester.sh
+++ b/tester/tester.sh
@@ -62,8 +62,8 @@ if [ "$rc" -ne "0" ]; then
     echo
     echo "Failed at creating output. Command: $main_cmd"
     if [ ! -z "$log_dir" ]; then
-	    [ ! -e "$log_out" ] && echo "*** stdout log: $log_out ***" && cat $log_out
-	    [ ! -e "$log_err" ] && echo "*** stderr log: $log_err ***" && cat $log_err
+	    [ -s "$log_out" ] && echo "*** stdout log: $log_out ***" && cat "$log_out"
+	    [ -s "$log_err" ] && echo "*** stderr log: $log_err ***" && cat "$log_err"
     fi
     echo
     echo "$test_name: Failed with exit code $rc"