Repository: andreypomortsev/yndx-python-handbook

Issue:
Feature: Add 3 solutions for 5.2 A-С
1. Add 3 solution for A-C problems.
2. Cover the solutions with tests (100%).
3. Update README.
4. Switch to poetry

Discussion:

Solution patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4a66a225..3ccedd33 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,9 +24,13 @@ jobs:
         with:
           python-version: ${{ matrix.python-version }}
 
-      - name: Install dependencies
+      - name: Install Poetry
         run: |
-          make install
+          pip install poetry
+
+      - name: Install dependencies using Poetry
+        run: |
+          poetry install --no-root
 
       - name: Run Flake8 linting
         run: |
diff --git a/Makefile b/Makefile
index 89c92cf5..d4a595bd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,27 +1,29 @@
 setup:
-	python3 -m venv .venv
-	@echo "\n\033[0;31mНе забудьте активировать виртуальную среду перед установкой библиотек!\033[0m\n"
+	pip3 install poetry -q
+	poetry install
+	poetry shell
 
-install:
-	pip install --upgrade pip && pip install -r requirements.txt
+clean:
+	find . -name '*.pyc' -delete
+	find . -name '__pycache__' -delete
 
 format:
-	black . --line-length=79 && isort .
+	poetry run black . --line-length=79 && poetry run isort .
 
 lint:
-	flake8 .
+	poetry run flake8 .
 
-test: lint 
-	pytest
+test: lint
+	poetry run pytest; $(MAKE) clean
 
 test-dir-%:
-	pytest tests/$*/
+	poetry run pytest tests/$*/; $(MAKE) clean
 
 test-file-%:
-	./fileTest.sh $*
+	poetry run ./fileTest.sh $*; $(MAKE) clean
 
 test-report-%:
-	pytest --cov-report=$*
+	poetry run pytest --cov-report=$*; $(MAKE) clean
 
 debug:
-	pytest -vv
+	poetry run pytest -vv; $(MAKE) clean
diff --git a/README.md b/README.md
index b137608f..5ccd8d5f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 ![Codacy grade (branch)](https://img.shields.io/codacy/grade/63f71a9c86ce4a0492af52c23628b78a/main)
 ![Build Status](https://github.com/andreypomortsev/yndx-python-handbook/actions/workflows/ci.yml/badge.svg)
 [![codecov](https://codecov.io/gh/andreypomortsev/yndx-python-handbook/branch/main/graph/badge.svg?token=WPUYVICKGT)](https://codecov.io/gh/andreypomortsev/yndx-python-handbook)
-![Python 3.10.14](https://img.shields.io/badge/Python-3.10.14-orange.svg)
-![Python 3.11.9](https://img.shields.io/badge/Python-3.11.9-yellow.svg)
+![Python 3.10](https://img.shields.io/badge/Python-3.10-orange.svg)
+![Python 3.11](https://img.shields.io/badge/Python-3.11-yellow.svg)
 ![Python 3.12](https://img.shields.io/badge/Python-3.12-green.svg)
 ![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)
 
@@ -19,9 +19,9 @@ _"Хендбук по Python поможет овладеть основным с
 
 ### Все протестированно на следующих версиях
 
-- [**Python 3.10.14**](https://www.python.org/downloads/release/python-31014/)
-- [**Python 3.11.9**](https://www.python.org/downloads/release/python-31014/)
-- [**Python 3.12**](https://www.python.org/downloads/release/python-3124/)
+- [**Python 3.10**](https://www.python.org/downloads/release/python-31015/)
+- [**Python 3.11**](https://www.python.org/downloads/release/python-31110/)
+- [**Python 3.12**](https://www.python.org/downloads/release/python-3126/)
 
 ### Клонируем репозиторий
 
@@ -30,32 +30,12 @@ git clone https://github.com/andreypomortsev/yndx-python-handbook
 cd yndx-python-handbook
 ```
 
-### Создаем виртуальную среду
+### Устанавлваем poetry и создаем виртуальую среду
 
 ```sh
 make setup
 ```
 
-### Активируем виртуальную среду
-
-- На macOS и Linux:
-
-  ```sh
-  source .venv/bin/activate
-  ```
-
-- На Windows:
-
-  ```PowerShell
-  .venv\Scripts\activate
-  ```
-
-### Устанавливаемм все необходимое
-
-```sh
-make install
-```
-
 ### Форматируем код по PEP8
 
 ```sh
@@ -70,6 +50,12 @@ make format
 make test
 ```
 
+Все тесты в дебаг моде:
+
+```sh
+make debug
+```
+
 Все тесты одного парагарафа (вместо `2.1` укажите нужный параграф) приведенный код запустит тесты для всех задач из параграфа [2.1](./solutions/2.1/):
 
 ```sh
@@ -91,7 +77,7 @@ make test-dir-2.1
 - На Windows:
 
   ```PowerShell
-  pytest tests\2.3\test_23_q.py
+  poetry run pytest tests\2.3\test_23_q.py && make clean
   ```
 
 Тесты с отчетом в `html`:
@@ -108,6 +94,19 @@ make test-report-html
 make test-report-xml
 ```
 
+### Запуск линтера flake8
+
+```sh
+make lint
+```
+
+### Запуск форматтера black и сортировки импортов isort
+
+```sh
+make format
+```
+
+
 Во время теста в папке `tests` временно создаются файлы `wrapped_*.py`.
 
 <details>
@@ -570,6 +569,28 @@ def main():
 
 </details>
 
+<details>
+<summary><h3>5.2 Волшебные методы, переопределение методов. Наследование</h3></summary>
+
+- [Теория Волшебные методы, переопределение методов. Наследование](https://education.yandex.ru/handbook/python/article/volshebnye-metody-pereopredelenie-metodov-nasledovanie)
+
+### [Тестовые данные для задач](./tests/data/test_data_52.py)
+
+| Решение              | Тесты                |
+|----------------------|----------------------|
+| А. [Классная точка 3.0](./solutions/5.2/51_a.py) | [✅](./tests/5.2/test_51_a.py) |
+| B. [Классная точка 4.0](./solutions/5.2/51_b.py) | [✅](./tests/5.2/test_51_b.py) |
+| C. [Классная точка 5.0](./solutions/5.2/51_c.py) | [✅](./tests/5.2/test_51_c.py) |
+| D. [Дроби v0.1](./solutions/5.2/51_d.py) | [✅](./tests/5.2/test_51_d.py) |
+| E. [Дроби v0.2](./solutions/5.2/51_e.py) | [✅](./tests/5.2/test_51_e.py) |
+| F. [Дроби v0.3](./solutions/5.2/51_f.py) | [✅](./tests/5.2/test_51_f.py) |
+| G. [Дроби v0.4](./solutions/5.2/51_g.py) | [✅](./tests/5.2/test_51_g.py) |
+| H. [Дроби v0.5](./solutions/5.2/51_h.py) | [✅](./tests/5.2/test_51_h.py) |
+| I. [Дроби v0.6](./solutions/5.2/51_i.py) | [✅](./tests/5.2/test_51_i.py) |
+| J. [Дроби v0.7](./solutions/5.2/51_j.py) | [✅](./tests/5.2/test_51_j.py) |
+
+</details>
+
 ## Создание Issue
 
 Если вы обнаружили ошибку, очепятку или баг в проекте, пожалуйста, создайте новый issue, следуя этому шаблону:
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 00000000..f53d3589
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,393 @@
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+
+[[package]]
+name = "black"
+version = "24.8.0"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
+    {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
+    {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
+    {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
+    {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
+    {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
+    {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
+    {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
+    {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
+    {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
+    {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
+    {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
+    {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
+    {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
+    {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
+    {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
+    {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
+    {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
+    {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
+    {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
+    {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
+    {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+packaging = ">=22.0"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "click"
+version = "8.1.7"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
+    {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "coverage"
+version = "7.6.1"
+description = "Code coverage measurement for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"},
+    {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"},
+    {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"},
+    {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"},
+    {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"},
+    {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"},
+    {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"},
+    {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"},
+    {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"},
+    {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"},
+    {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"},
+    {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"},
+    {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"},
+    {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"},
+    {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"},
+    {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"},
+    {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"},
+    {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"},
+    {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"},
+    {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"},
+    {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"},
+    {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"},
+    {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"},
+    {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"},
+    {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"},
+    {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"},
+    {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"},
+    {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"},
+    {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"},
+    {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"},
+    {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"},
+    {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"},
+    {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"},
+    {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"},
+    {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"},
+    {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"},
+    {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"},
+    {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"},
+    {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"},
+    {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"},
+    {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"},
+    {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"},
+    {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"},
+    {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"},
+    {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"},
+    {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"},
+    {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"},
+    {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"},
+    {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"},
+    {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"},
+    {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"},
+    {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"},
+    {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"},
+    {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"},
+    {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"},
+    {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"},
+    {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"},
+    {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"},
+    {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"},
+    {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"},
+    {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"},
+    {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"},
+    {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"},
+    {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"},
+    {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"},
+    {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"},
+    {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"},
+    {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"},
+    {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"},
+    {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"},
+    {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"},
+    {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"},
+]
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.2"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
+    {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "flake8"
+version = "7.1.1"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = ">=3.8.1"
+files = [
+    {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"},
+    {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"},
+]
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.12.0,<2.13.0"
+pyflakes = ">=3.2.0,<3.3.0"
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "isort"
+version = "5.13.2"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+files = [
+    {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+    {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+    {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+description = "Type system extensions for programs checked with the mypy type checker."
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
+    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+]
+
+[[package]]
+name = "packaging"
+version = "24.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+    {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+    {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.3.6"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
+    {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
+]
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.11.2)"]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+    {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pycodestyle"
+version = "2.12.1"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"},
+    {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"},
+]
+
+[[package]]
+name = "pyflakes"
+version = "3.2.0"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
+    {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
+]
+
+[[package]]
+name = "pytest"
+version = "8.3.3"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
+    {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.5,<2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "5.0.0"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
+    {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
+]
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pytest-spec"
+version = "4.0.0"
+description = "Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pytest_spec-4.0.0-py3-none-any.whl", hash = "sha256:5d85ed29d9240722dfe778bed80022b917154c8200020864a61bfa65f138f70a"},
+    {file = "pytest_spec-4.0.0.tar.gz", hash = "sha256:71c9985e97d090a69b1f1b7adb64e7a208fb1ac42432ce9566c32cdd6b44c1ad"},
+]
+
+[package.extras]
+deploy = ["build", "twine"]
+test = ["pytest", "pytest-describe"]
+
+[[package]]
+name = "tomli"
+version = "2.0.2"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
+    {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+    {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.10"
+content-hash = "a2f0c3ecd1702b6a3b87b582e3d31fef8ecccd4a459bd228a8cb812d728e622a"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..6190956b
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,23 @@
+[tool.poetry]
+name = "yndx-python-handbook"
+version = "0.1.0"
+description = ""
+authors = ["Andrei Pomortsev"]
+license = "MIT"
+readme = "README.md"
+package-mode = false
+
+[tool.poetry.dependencies]
+python = "^3.10"
+pytest = "^8.3.3"
+pytest-cov = "^5.0.0"
+pytest-spec = "^4.0.0"
+flake8 = "^7.1.1"
+black = "^24.8.0"
+isort = "^5.13.2"
+coverage = "^7.6.1"
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/requirements.txt b/requirements.txt
index a57ab763..4eaaabc7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,9 +3,6 @@ pytest==8.2.2
 pytest-cov==5.0.0
 pytest-spec==3.2.0
 
-# Performance checking
-psutil==6.0.0
-
 # Test coverage
 coverage==7.6.0
 
diff --git a/solutions/5.1/51_e.py b/solutions/5.1/51_e.py
index cee62206..1a42b6b1 100644
--- a/solutions/5.1/51_e.py
+++ b/solutions/5.1/51_e.py
@@ -1,18 +1,17 @@
 class Rectangle:
-    def __init__(
-        self,
-        point_one: tuple[float | int, float | int],
-        point_two: tuple[float | int, float | int],
-    ):
+    def __init__(self, point_one: tuple, point_two: tuple) -> None:
         self.x_one, self.y_one = point_one
         self.x_two, self.y_two = point_two
+
         self.height = abs(self.y_one - self.y_two)
         self.width = abs(self.x_one - self.x_two)
 
-    def perimeter(self) -> float | int:
-        calculated_perimeter = (self.height + self.width) * 2
-        return round(calculated_perimeter, 2)
+    def perimeter(self) -> float:
+        calc_perimeter = (self.height + self.width) * 2
+
+        return round(calc_perimeter, 2)
+
+    def area(self) -> float:
+        calc_area = self.height * self.width
 
-    def area(self) -> float | int:
-        calculated_area = self.height * self.width
-        return round(calculated_area, 2)
+        return round(calc_area, 2)
diff --git a/solutions/5.1/51_f.py b/solutions/5.1/51_f.py
index 72c27b8d..518117b1 100644
--- a/solutions/5.1/51_f.py
+++ b/solutions/5.1/51_f.py
@@ -7,32 +7,37 @@ def __init__(self, point_one: tuple, point_two: tuple) -> None:
         self.width = abs(self.x_one - self.x_two)
 
     def perimeter(self) -> float:
-        return round((self.height + self.width) * 2, 2)
+        calc_perimeter = (self.height + self.width) * 2
+
+        return round(calc_perimeter, 2)
 
     def area(self) -> float:
-        return round(self.height * self.width, 2)
+        calc_area = self.height * self.width
+
+        return round(calc_area, 2)
 
     def get_pos(self) -> tuple:
         x = min(self.x_one, self.x_two)
         y = max(self.y_one, self.y_two)
+
         return round(x, 2), round(y, 2)
 
     def get_size(self) -> tuple:
         return round(self.width, 2), round(self.height, 2)
 
-    def move(self, dx: float, dy: float) -> None:
+    def move(self, dx: float | int, dy: float | int) -> None:
         self.x_one += dx
         self.x_two += dx
         self.y_one += dy
         self.y_two += dy
 
-    def resize(self, width: float, height: float) -> None:
+    def resize(self, width: float | int, height: float | int) -> None:
         self.width = width
         self.height = height
 
         x, y = self.get_pos()
 
         self.x_one = x
-        self.x_two = x + self.width
         self.y_one = y
+        self.x_two = x + self.width
         self.y_two = y - self.height
diff --git a/solutions/5.1/51_g.py b/solutions/5.1/51_g.py
index 0623fcc0..3613b8f1 100644
--- a/solutions/5.1/51_g.py
+++ b/solutions/5.1/51_g.py
@@ -7,36 +7,34 @@ def __init__(self, point_one: tuple, point_two: tuple) -> None:
         self.width = abs(self.x_one - self.x_two)
 
     def perimeter(self) -> float:
-        return round((self.height + self.width) * 2, 2)
+        calc_perimeter = (self.height + self.width) * 2
+
+        return round(calc_perimeter, 2)
 
     def area(self) -> float:
-        return round(self.height * self.width, 2)
+        calc_area = self.height * self.width
+
+        return round(calc_area, 2)
 
     def get_pos(self) -> tuple:
         x = min(self.x_one, self.x_two)
         y = max(self.y_one, self.y_two)
+
         return round(x, 2), round(y, 2)
 
     def get_size(self) -> tuple:
         return round(self.width, 2), round(self.height, 2)
 
-    def move(self, dx: float, dy: float) -> None:
+    def move(self, dx: float | int, dy: float | int) -> None:
         self.x_one += dx
         self.x_two += dx
         self.y_one += dy
         self.y_two += dy
 
-    def resize(self, width: float, height: float) -> None:
+    def resize(self, width: float | int, height: float | int) -> None:
         self.width = width
         self.height = height
 
-        x, y = self.get_pos()
-
-        self.x_one = x
-        self.y_one = y
-        self.x_two = x + self.width
-        self.y_two = y - self.height
-
     def turn(self) -> None:
         center_x, center_y = self._get_center()
 
@@ -51,7 +49,7 @@ def turn(self) -> None:
 
         self.width, self.height = self.height, self.width
 
-    def scale(self, factor: float) -> None:
+    def scale(self, factor: float | int) -> None:
         self.width = round(self.width * factor, 2)
         self.height = round(self.height * factor, 2)
 
diff --git a/solutions/5.2/52_a.py b/solutions/5.2/52_a.py
new file mode 100644
index 00000000..81eb73aa
--- /dev/null
+++ b/solutions/5.2/52_a.py
@@ -0,0 +1,23 @@
+class Point:
+    def __init__(self, x: float | int, y: float | int) -> None:
+        self.x = x
+        self.y = y
+
+    def move(self, x: float | int, y: float | int) -> None:
+        self.x += x
+        self.y += y
+
+    def length(self, other: "Point") -> float | int:
+        distance_squared = (other.x - self.x) ** 2 + (other.y - self.y) ** 2
+        return round(distance_squared**0.5, 2)
+
+
+class PatchedPoint(Point):
+    def __init__(self, *coordinates) -> None:
+        if not coordinates:
+            x, y = 0, 0
+        elif isinstance(coordinates[0], tuple):
+            x, y = coordinates[0]
+        else:
+            x, y = coordinates
+        super().__init__(x, y)
diff --git a/solutions/5.2/52_b.py b/solutions/5.2/52_b.py
new file mode 100644
index 00000000..57c9aac3
--- /dev/null
+++ b/solutions/5.2/52_b.py
@@ -0,0 +1,29 @@
+class Point:
+    def __init__(self, x: float | int, y: float | int) -> None:
+        self.x = x
+        self.y = y
+
+    def move(self, x: float | int, y: float | int) -> None:
+        self.x += x
+        self.y += y
+
+    def length(self, other: "Point") -> float | int:
+        distance_squared = (other.x - self.x) ** 2 + (other.y - self.y) ** 2
+        return round(distance_squared**0.5, 2)
+
+
+class PatchedPoint(Point):
+    def __init__(self, *coordinates) -> None:
+        if not coordinates:
+            x, y = 0, 0
+        elif isinstance(coordinates[0], tuple):
+            x, y = coordinates[0]
+        else:
+            x, y = coordinates
+        super().__init__(x, y)
+
+    def __str__(self) -> str:
+        return f"({self.x}, {self.y})"
+
+    def __repr__(self) -> str:
+        return f"PatchedPoint({self.x}, {self.y})"
diff --git a/solutions/5.2/52_c.py b/solutions/5.2/52_c.py
new file mode 100644
index 00000000..d9e395fd
--- /dev/null
+++ b/solutions/5.2/52_c.py
@@ -0,0 +1,39 @@
+class Point:
+    def __init__(self, x: float | int, y: float | int) -> None:
+        self.x = x
+        self.y = y
+
+    def move(self, x: float | int, y: float | int) -> None:
+        self.x += x
+        self.y += y
+
+    def length(self, other: "Point") -> float | int:
+        distance_squared = (other.x - self.x) ** 2 + (other.y - self.y) ** 2
+        return round(distance_squared**0.5, 2)
+
+
+class PatchedPoint(Point):
+    def __init__(self, *coordinates) -> None:
+        if not coordinates:
+            x, y = 0, 0
+        elif isinstance(coordinates[0], tuple):
+            x, y = coordinates[0]
+        else:
+            x, y = coordinates
+        super().__init__(x, y)
+
+    def __str__(self) -> str:
+        return f"({self.x}, {self.y})"
+
+    def __repr__(self) -> str:
+        return f"PatchedPoint({self.x}, {self.y})"
+
+    def __add__(self, coordinates: tuple) -> "PatchedPoint":
+        x, y = coordinates
+        return PatchedPoint(self.x + x, self.y + y)
+
+    def __iadd__(self, coordinates: tuple) -> "PatchedPoint":
+        x, y = coordinates
+        self.x += x
+        self.y += y
+        return self


Test patch:
diff --git a/tests/5.1/test_51_a.py b/tests/5.1/test_51_a.py
index 0c959a5f..bb15e960 100644
--- a/tests/5.1/test_51_a.py
+++ b/tests/5.1/test_51_a.py
@@ -1,5 +1,5 @@
 from types import ModuleType
-from typing import Callable
+from typing import Callable, Tuple
 
 import pytest
 
@@ -8,19 +8,19 @@
 
 
 @pytest.mark.parametrize(
-    "x, y, _",
+    "coordinates, _",
     point_init,
     ids=[i[-1] for i in point_init],
 )
 def test_point_class(
     load_module: Callable[[str], ModuleType],
     request: pytest.FixtureRequest,
-    x: int | float,
-    y: int | float,
+    coordinates: Tuple[int | float, int | float],
     _: str,
 ) -> None:
     file_path, _ = utils.get_tested_file_details(request)
     solution_module = load_module(file_path)
+    x, y = coordinates
     point = solution_module.Point(x, y)
 
     assert point.x == x
diff --git a/tests/5.1/test_51_b.py b/tests/5.1/test_51_b.py
index 0255dc72..08993059 100644
--- a/tests/5.1/test_51_b.py
+++ b/tests/5.1/test_51_b.py
@@ -8,22 +8,23 @@
 
 
 @pytest.mark.parametrize(
-    "x, y, _",
+    "coordinates, _",
     point_init,
     ids=[i[-1] for i in point_init],
 )
-def test_point_class_init(
+def test_point_class(
     load_module: Callable[[str], ModuleType],
     request: pytest.FixtureRequest,
-    x: int | float,
-    y: int | float,
+    coordinates: Tuple[int | float, int | float],
     _: str,
 ) -> None:
     file_path, _ = utils.get_tested_file_details(request)
     solution_module = load_module(file_path)
+    x, y = coordinates
     point = solution_module.Point(x, y)
 
-    assert (point.x, point.y) == (x, y)
+    assert point.x == x
+    assert point.y == y
 
 
 @pytest.mark.parametrize(
diff --git a/tests/5.2/test_52_a.py b/tests/5.2/test_52_a.py
new file mode 100644
index 00000000..caa6a711
--- /dev/null
+++ b/tests/5.2/test_52_a.py
@@ -0,0 +1,92 @@
+from types import ModuleType
+from typing import Callable, Optional, Tuple
+
+import pytest
+
+from tests import utils
+from tests.data.test_data_51 import point_init, point_length, point_move
+from tests.data.test_data_52 import patched_point_init
+
+patched_point_init.extend(point_init)
+
+
+@pytest.mark.parametrize(
+    "coordinates, _",
+    patched_point_init,
+    ids=[i[-1] for i in patched_point_init],
+)
+def test_patched_point_class(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    if not coordinates:
+        x, y = 0, 0
+    elif isinstance(coordinates[0], tuple):
+        x, y = coordinates[0]
+    else:
+        x, y = coordinates
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    assert point.x == x
+    assert point.y == y
+
+
+@pytest.mark.parametrize(
+    "x_y, x_y_move, _",
+    point_move,
+    ids=[i[-1] for i in point_move],
+)
+def test_patched_point_move(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    x_y: Tuple[int | float],
+    x_y_move: Tuple[int | float],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x, y = x_y
+    point = solution_module.PatchedPoint(x, y)
+
+    assert (point.x, point.y) == (x, y)
+
+    x_move, y_move = x_y_move
+    point.move(x_move, y_move)
+
+    x += x_move
+    y += y_move
+
+    assert (point.x, point.y) == (x, y)
+
+
+@pytest.mark.parametrize(
+    "xs_ys, expected_output, _",
+    point_length,
+    ids=[i[-1] for i in point_length],
+)
+def test_patched_point_length(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    xs_ys: Tuple[int | float],
+    expected_output: int | float,
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x_one, y_one, x_two, y_two = xs_ys
+    point_one = solution_module.PatchedPoint(x_one, y_one)
+    point_two = solution_module.PatchedPoint(x_two, y_two)
+
+    assert (point_one.x, point_one.y) == (x_one, y_one)
+    assert (point_two.x, point_two.y) == (x_two, y_two)
+
+    assert point_one.length(point_two) == expected_output
+    assert point_two.length(point_one) == expected_output
diff --git a/tests/5.2/test_52_b.py b/tests/5.2/test_52_b.py
new file mode 100644
index 00000000..7204f6c7
--- /dev/null
+++ b/tests/5.2/test_52_b.py
@@ -0,0 +1,114 @@
+from types import ModuleType
+from typing import Callable, Optional, Tuple
+
+import pytest
+
+from tests import utils
+from tests.data.test_data_51 import point_init, point_length, point_move
+from tests.data.test_data_52 import patched_point_init, patched_point_str_repr
+
+patched_point_init.extend(point_init)
+
+
+@pytest.mark.parametrize(
+    "coordinates, _",
+    patched_point_init,
+    ids=[i[-1] for i in patched_point_init],
+)
+def test_patched_point_class(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    if not coordinates:
+        x, y = 0, 0
+    elif isinstance(coordinates[0], tuple):
+        x, y = coordinates[0]
+    else:
+        x, y = coordinates
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    assert point.x == x
+    assert point.y == y
+
+
+@pytest.mark.parametrize(
+    "x_y, x_y_move, _",
+    point_move,
+    ids=[i[-1] for i in point_move],
+)
+def test_patched_point_move(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    x_y: Tuple[int | float],
+    x_y_move: Tuple[int | float],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x, y = x_y
+    point = solution_module.PatchedPoint(x, y)
+
+    assert (point.x, point.y) == (x, y)
+
+    x_move, y_move = x_y_move
+    point.move(x_move, y_move)
+
+    x += x_move
+    y += y_move
+
+    assert (point.x, point.y) == (x, y)
+
+
+@pytest.mark.parametrize(
+    "xs_ys, expected_output, _",
+    point_length,
+    ids=[i[-1] for i in point_length],
+)
+def test_patched_point_length(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    xs_ys: Tuple[int | float],
+    expected_output: int | float,
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x_one, y_one, x_two, y_two = xs_ys
+    point_one = solution_module.PatchedPoint(x_one, y_one)
+    point_two = solution_module.PatchedPoint(x_two, y_two)
+
+    assert (point_one.x, point_one.y) == (x_one, y_one)
+    assert (point_two.x, point_two.y) == (x_two, y_two)
+
+    assert point_one.length(point_two) == expected_output
+    assert point_two.length(point_one) == expected_output
+
+
+@pytest.mark.parametrize(
+    "coordinates, str_out, repr_out, _",
+    patched_point_str_repr,
+    ids=[i[-1] for i in patched_point_str_repr],
+)
+def test_patched_point_str_repr(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    str_out: str,
+    repr_out: str,
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    assert str(point) == str_out
+    assert repr(point) == repr_out
diff --git a/tests/5.2/test_52_c.py b/tests/5.2/test_52_c.py
new file mode 100644
index 00000000..2445ea88
--- /dev/null
+++ b/tests/5.2/test_52_c.py
@@ -0,0 +1,161 @@
+from types import ModuleType
+from typing import Callable, Optional, Tuple
+
+import pytest
+
+from tests import utils
+from tests.data.test_data_51 import point_init, point_length, point_move
+from tests.data.test_data_52 import (patched_point_add, patched_point_init,
+                                     patched_point_str_repr)
+
+patched_point_init.extend(point_init)
+
+
+@pytest.mark.parametrize(
+    "coordinates, _",
+    patched_point_init,
+    ids=[i[-1] for i in patched_point_init],
+)
+def test_patched_point_class(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    if not coordinates:
+        x, y = 0, 0
+    elif isinstance(coordinates[0], tuple):
+        x, y = coordinates[0]
+    else:
+        x, y = coordinates
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    assert point.x == x
+    assert point.y == y
+
+
+@pytest.mark.parametrize(
+    "x_y, x_y_move, _",
+    point_move,
+    ids=[i[-1] for i in point_move],
+)
+def test_patched_point_move(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    x_y: Tuple[int | float],
+    x_y_move: Tuple[int | float],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x, y = x_y
+    point = solution_module.PatchedPoint(x, y)
+
+    assert (point.x, point.y) == (x, y)
+
+    x_move, y_move = x_y_move
+    point.move(x_move, y_move)
+
+    x += x_move
+    y += y_move
+
+    assert (point.x, point.y) == (x, y)
+
+
+@pytest.mark.parametrize(
+    "xs_ys, expected_output, _",
+    point_length,
+    ids=[i[-1] for i in point_length],
+)
+def test_patched_point_length(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    xs_ys: Tuple[int | float],
+    expected_output: int | float,
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    x_one, y_one, x_two, y_two = xs_ys
+    point_one = solution_module.PatchedPoint(x_one, y_one)
+    point_two = solution_module.PatchedPoint(x_two, y_two)
+
+    assert (point_one.x, point_one.y) == (x_one, y_one)
+    assert (point_two.x, point_two.y) == (x_two, y_two)
+
+    assert point_one.length(point_two) == expected_output
+    assert point_two.length(point_one) == expected_output
+
+
+@pytest.mark.parametrize(
+    "coordinates, str_out, repr_out, _",
+    patched_point_str_repr,
+    ids=[i[-1] for i in patched_point_str_repr],
+)
+def test_patched_point_str_repr(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    str_out: str,
+    repr_out: str,
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    assert str(point) == str_out
+    assert repr(point) == repr_out
+
+
+@pytest.mark.parametrize(
+    "coordinates, dx_dy, _",
+    patched_point_add,
+    ids=[i[-1] for i in patched_point_add],
+)
+def test_patched_point_add(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    dx_dy: Tuple[int | float, int | float],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    point = solution_module.PatchedPoint(*coordinates)
+
+    new_point = point + dx_dy
+
+    assert new_point is not point
+    assert new_point.x == point.x + dx_dy[0]
+    assert new_point.y == point.y + dx_dy[1]
+
+
+@pytest.mark.parametrize(
+    "coordinates, dx_dy, _",
+    patched_point_add,
+    ids=[i[-1] for i in patched_point_add],
+)
+def test_patched_point_iadd(
+    load_module: Callable[[str], ModuleType],
+    request: pytest.FixtureRequest,
+    coordinates: Optional[Tuple[int | float, int | float]],
+    dx_dy: Tuple[int | float, int | float],
+    _: str,
+) -> None:
+    file_path, _ = utils.get_tested_file_details(request)
+    solution_module = load_module(file_path)
+
+    first_point = second_point = solution_module.PatchedPoint(*coordinates)
+
+    first_point += dx_dy
+
+    assert first_point is second_point
diff --git a/tests/data/test_data_51.py b/tests/data/test_data_51.py
index 2a28db4a..d7138213 100644
--- a/tests/data/test_data_51.py
+++ b/tests/data/test_data_51.py
@@ -1,15 +1,16 @@
 point_init = [
-    (3, 5, "first open test"),
-    (2, -7, "second open test"),
-    (0.2, -0.7, "floats"),
-    (0, 1, "zero one"),
-    (1, 0, "one zero"),
-    (1000000000000, 99999999999999999, "big numbers"),
-    (0.000000000001, 0.0000000000000009, "small numbers"),
+    ((3, 5), "first open test"),
+    ((2, -7), "second open test"),
+    ((0.2, -0.7), "floats"),
+    ((0, 1), "zero one"),
+    ((1, 0), "one zero"),
+    ((1000000000000, 99999999999999999), "big numbers"),
+    ((0.000000000001, 0.0000000000000009), "small numbers"),
 ]
 
 point_move = [
     ((3, 5), (2, -3), "first open test"),
+    ((0, 0), (2, -3), "first open test 52"),
     ((-3, -9), (-12, -5), "negative numbers"),
     ((-3, -9), (0, 0), "negative numbers zero move"),
     ((3, 9), (0, 0), "positive number zero move"),
@@ -311,7 +312,7 @@
             True,
             {"one": 1, "two": 2},
             ("tuple!", ""),
-            []
+            [],
         ],
         "different data types",
     ),
diff --git a/tests/data/test_data_52.py b/tests/data/test_data_52.py
new file mode 100644
index 00000000..3cb960a3
--- /dev/null
+++ b/tests/data/test_data_52.py
@@ -0,0 +1,30 @@
+patched_point_init = [
+    (tuple(), "first open test 5,2"),
+    ((2, -7), "ints"),
+    ((0.213, -13.7), "floats"),
+    (((0.2, -0.7),), "tuple of floats"),
+    (((2, -7),), "tuple of ints"),
+]
+
+patched_point_str_repr = [
+    (((2, -7),), "(2, -7)", "PatchedPoint(2, -7)", "second open test 1"),
+    ((7, 9), "(7, 9)", "PatchedPoint(7, 9)", "second open test 2"),
+    (
+        ((0.07, 0.09),),
+        "(0.07, 0.09)",
+        "PatchedPoint(0.07, 0.09)",
+        "tuple of floats",
+    ),
+    ((7.21, 9.02), "(7.21, 9.02)", "PatchedPoint(7.21, 9.02)", "floats"),
+    (tuple(), "(0, 0)", "PatchedPoint(0, 0)", "w/o coordinates"),
+    ((0, 0), "(0, 0)", "PatchedPoint(0, 0)", "zero coordinates"),
+    (((0, 0),), "(0, 0)", "PatchedPoint(0, 0)", "seros in tuple"),
+]
+
+patched_point_add = [
+    (tuple(), (2, -3), "first open test"),
+    ((-2, 3), (2, -3), "zero point"),
+    (((0.012, 3212),), (0, 0), "zero difference with tuple of mixed"),
+    (tuple(), (0, 0), "zero difference w/o coordinates"),
+    ((121, 989), (0, 0), "zero difference with ints"),
+]