From 63d82f6e45a10389974df84444e1b2513a8b32b8 Mon Sep 17 00:00:00 2001
From: 1vanK <1vanK@users.noreply.github.com>
Date: Wed, 13 Mar 2024 06:45:28 +0300
Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BA=D0=BB=D0=B5=D0=B5=D0=BD=D0=BD?=
=?UTF-8?q?=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=D1=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.editorconfig | 15 +
.gitattributes | 8 +
.github/workflows/tester.yml | 120 ++++++
.gitignore | 6 +
CMakeLists.txt | 95 ++++
build_linux.sh | 17 +
build_mingw.bat | 23 +
build_vs.bat | 25 ++
docs/1_basics.md | 134 ++++++
docs/1_basics_add_1.cpp | 83 ++++
docs/1_basics_add_2.cpp | 82 ++++
docs/1_basics_div.cpp | 219 ++++++++++
docs/1_basics_mul_1.cpp | 119 ++++++
docs/1_basics_mul_2.cpp | 147 +++++++
docs/1_basics_sub_1.cpp | 153 +++++++
docs/1_basics_sub_2.cpp | 150 +++++++
docs/2_div_len.cpp | 166 +++++++
docs/2_div_len.md | 64 +++
docs/3_brute_force_div.md | 87 ++++
docs/3_brute_force_div_1.cpp | 237 ++++++++++
docs/3_brute_force_div_2.cpp | 259 +++++++++++
docs/4_long_div.md | 185 ++++++++
docs/4_long_div_1.cpp | 299 +++++++++++++
docs/4_long_div_2.cpp | 332 ++++++++++++++
docs/4_long_div_3.cpp | 347 +++++++++++++++
docs/4_long_div_4.cpp | 351 +++++++++++++++
docs/4_long_div_5.cpp | 371 ++++++++++++++++
docs/libs.md | 38 ++
docs/markdown.md | 28 ++
docs/names.md | 5 +
lib/dv_big_int.cpp | 808 +++++++++++++++++++++++++++++++++++
lib/dv_big_int.hpp | 125 ++++++
license.txt | 21 +
logo.png | Bin 0 -> 43171 bytes
readme.md | 17 +
remove_spaces.py | 40 ++
tester/force_assert.hpp | 16 +
tester/tester.cpp | 295 +++++++++++++
38 files changed, 5487 insertions(+)
create mode 100644 .editorconfig
create mode 100644 .gitattributes
create mode 100644 .github/workflows/tester.yml
create mode 100644 .gitignore
create mode 100644 CMakeLists.txt
create mode 100755 build_linux.sh
create mode 100644 build_mingw.bat
create mode 100644 build_vs.bat
create mode 100644 docs/1_basics.md
create mode 100644 docs/1_basics_add_1.cpp
create mode 100644 docs/1_basics_add_2.cpp
create mode 100644 docs/1_basics_div.cpp
create mode 100644 docs/1_basics_mul_1.cpp
create mode 100644 docs/1_basics_mul_2.cpp
create mode 100644 docs/1_basics_sub_1.cpp
create mode 100644 docs/1_basics_sub_2.cpp
create mode 100644 docs/2_div_len.cpp
create mode 100644 docs/2_div_len.md
create mode 100644 docs/3_brute_force_div.md
create mode 100644 docs/3_brute_force_div_1.cpp
create mode 100644 docs/3_brute_force_div_2.cpp
create mode 100644 docs/4_long_div.md
create mode 100644 docs/4_long_div_1.cpp
create mode 100644 docs/4_long_div_2.cpp
create mode 100644 docs/4_long_div_3.cpp
create mode 100644 docs/4_long_div_4.cpp
create mode 100644 docs/4_long_div_5.cpp
create mode 100644 docs/libs.md
create mode 100644 docs/markdown.md
create mode 100644 docs/names.md
create mode 100644 lib/dv_big_int.cpp
create mode 100644 lib/dv_big_int.hpp
create mode 100644 license.txt
create mode 100644 logo.png
create mode 100644 readme.md
create mode 100644 remove_spaces.py
create mode 100644 tester/force_assert.hpp
create mode 100644 tester/tester.cpp
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..4f6de69
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+# Заставляем VS и другие IDE сохранять файлы в кодировке UTF-8
+# https://editorconfig.org
+
+# В родительских папках .editorconfig искаться не будет
+root = true
+
+[*]
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+
+# Не меняет формат концов строк. Может возникнуть конфликт с настройками git
+#end_of_line = lf
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9a1189b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+# Автоматически нормализуем концы строк, если у пользователя не настроен параметр autocrlf
+# https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
+# https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/
+* text=auto
+
+# Батники с юниксовыми концами строк глючат
+*.bat text eol=crlf
+*.cmd text eol=crlf
diff --git a/.github/workflows/tester.yml b/.github/workflows/tester.yml
new file mode 100644
index 0000000..af568c2
--- /dev/null
+++ b/.github/workflows/tester.yml
@@ -0,0 +1,120 @@
+name: Tester
+
+on:
+ push:
+ paths-ignore: 'docs/**'
+ pull_request:
+ paths-ignore: 'docs/**'
+ workflow_dispatch:
+
+jobs:
+ linux:
+ runs-on: ubuntu-24.04
+
+ strategy:
+ fail-fast: false
+ matrix:
+ compiler:
+ - {
+ id: gcc,
+ c: gcc-13,
+ cxx: g++-13,
+ }
+ - {
+ id: clang,
+ c: clang-16,
+ cxx: clang++-16,
+ }
+ build_type: [debug, release]
+
+ name: 🐧-${{ matrix.compiler.id }}-${{ matrix.build_type }}
+
+ steps:
+ - name: Скачиваем репозиторий
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ path: repo
+
+ - name: Генерируем проекты
+ run: |
+ cmake repo -B build -G "Unix Makefiles" \
+ -D CMAKE_C_COMPILER=${{ matrix.compiler.c }} -D CMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} \
+ -D CMAKE_BUILD_TYPE=${{ matrix.build_type }}
+
+ - name: Компилируем
+ run: |
+ cmake --build build
+
+ - name: CTest
+ run: |
+ ctest --verbose --test-dir build --timeout 60
+
+ windows:
+ runs-on: windows-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ compiler: [vs, mingw]
+ build_type: [debug, release]
+
+ name: 🔲-${{ matrix.compiler }}-${{ matrix.build_type }}
+
+ steps:
+ - name: Устанавливаем MinGW
+ if: matrix.compiler == 'mingw'
+ uses: msys2/setup-msys2@v2
+ with:
+ update: true
+ install: mingw-w64-x86_64-toolchain
+
+ - name: Добавляем в PATH путь к MinGW
+ if: matrix.compiler == 'mingw'
+ shell: bash
+ run: echo "${RUNNER_TEMP}/msys64/mingw64/bin" >> $GITHUB_PATH
+
+ - name: Скачиваем репозиторий
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ path: repo
+
+ - name: Генерируем проекты
+ shell: bash
+ run: |
+ args=(repo -B build)
+
+ if [ "${{ matrix.compiler }}" == "vs" ]
+ then
+ args+=(-G "Visual Studio 17 2022")
+ else
+ args+=(-G "MinGW Makefiles")
+ args+=(-D CMAKE_BUILD_TYPE=${{ matrix.build_type }})
+ fi
+
+ cmake "${args[@]}"
+
+ - name: Компилируем
+ shell: bash
+ run: |
+ args=(--build build)
+
+ if [ "${{ matrix.compiler }}" == "vs" ]
+ then
+ args+=(--config ${{ matrix.build_type }})
+ fi
+
+ cmake "${args[@]}"
+
+ - name: CTest
+ shell: bash
+ run: |
+ args=(--verbose --test-dir build --timeout 60)
+
+ if [ "${{ matrix.compiler }}" == "vs" ]
+ then
+ args+=(-C ${{ matrix.build_type }})
+ fi
+
+ ctest "${args[@]}"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..55086dc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# https://git-scm.com/docs/gitignore
+# https://www.atlassian.com/ru/git/tutorials/saving-changes/gitignore
+
+# Игнорируем папки, которые создаёт VS Code
+/.vscode/
+/build/
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..56b523a
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,95 @@
+# Указываем минимальную версию CMake
+cmake_minimum_required(VERSION 3.16)
+
+# Название проекта
+project(dv_big_int)
+
+option(BIG_INT_ONLY_LIB "Не компилировать Тестер" OFF)
+
+# Версия стандарта C++
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+# Если используется одноконфигурационный генератор и конфигурация не указана
+if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
+ # то конфигурацией по умолчанию будет Release
+ set(CMAKE_BUILD_TYPE Release)
+ # Нельзя оставлять переменную CMAKE_BUILD_TYPE пустой (подробности в Dviglo2D)
+endif()
+
+# Указываем Студии на то, что исходники в кодировке UTF-8.
+# Это позволяет писать U'🍌'. У других компиляторов, похоже, нет с этим проблем
+# https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2295r6.pdf
+if(MSVC)
+ add_compile_options(/utf-8)
+endif()
+
+# Выводим больше предупреждений
+if(MSVC)
+ add_compile_options(/W4)
+else()
+ add_compile_options(-Wall -Wextra -Wpedantic)
+
+ if(NOT MINGW)
+ add_compile_options(-fsanitize=undefined)
+ add_link_options(-fsanitize=undefined)
+ endif()
+endif()
+
+# Статически линкуем библиотеки, чтобы не копировать dll-ки
+if(MINGW)
+ add_link_options(-static)
+endif()
+
+# Включаем поддержку папок в IDE
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+# ============================================== Библиотека ==============================================
+
+# Название таргета
+set(target_name dv_big_int)
+
+# Создаём список файлов
+file(GLOB source_files lib/*)
+
+# Создаём статическую библиотеку
+add_library(${target_name} STATIC ${source_files})
+
+# Делаем заголовочные файлы доступными библиотеке и приложениям
+target_include_directories(${target_name} PUBLIC lib)
+
+# Заставляем VS отображать дерево каталогов
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${source_files})
+
+# ============================================== Тестер ==============================================
+
+if(NOT BIG_INT_ONLY_LIB)
+ # Название таргета
+ set(target_name tester)
+
+ # Создаём список файлов
+ file(GLOB source_files tester/*)
+
+ # Создаём приложение
+ add_executable(${target_name} ${source_files})
+
+ # Подключаем библиотеку
+ target_link_libraries(${target_name} PRIVATE dv_big_int)
+
+ # Отладочная версия приложения будет иметь суффикс _d
+ set_property(TARGET ${target_name} PROPERTY DEBUG_POSTFIX _d)
+
+ # Заставляем VS отображать дерево каталогов
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${source_files})
+
+ # Включаем тестирование
+ enable_testing()
+
+ # Добавляем приложение в список тестируемых
+ add_test(NAME ${target_name} COMMAND ${target_name})
+
+ # В Visual Studio таргет будет назначен стартовым вместо ALL_BUILD,
+ # чтобы потом не делать это вручную при отладке приложения
+ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${target_name})
+endif()
diff --git a/build_linux.sh b/build_linux.sh
new file mode 100755
index 0000000..34a5807
--- /dev/null
+++ b/build_linux.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+build_type="-D CMAKE_BUILD_TYPE=Debug"
+#build_type="-D CMAKE_BUILD_TYPE=Release"
+#build_type="-D CMAKE_BUILD_TYPE=MinSizeRel"
+#build_type="-D CMAKE_BUILD_TYPE=RelWithDebInfo"
+
+compiler="-D CMAKE_C_COMPILER=gcc-13 -D CMAKE_CXX_COMPILER=g++-13"
+#compiler="-D CMAKE_C_COMPILER=clang-15 -D CMAKE_CXX_COMPILER=clang++-15"
+
+repo_dir=$(dirname "$0")
+
+# Генерируем проект
+cmake "$repo_dir" -B "$repo_dir/../build" -G "Unix Makefiles" $build_type $compiler
+
+# Компилируем проект
+cmake --build "$repo_dir/../build"
diff --git a/build_mingw.bat b/build_mingw.bat
new file mode 100644
index 0000000..bbaf236
--- /dev/null
+++ b/build_mingw.bat
@@ -0,0 +1,23 @@
+:: Меняем кодировку консоли на UTF-8
+chcp 65001
+
+:: Указываем путь к cmake.exe и MinGW. Без system32 в PATH ссылки на папки не создаются
+set "PATH=%SystemRoot%\system32;c:\programs\cmake\bin;c:\msys64\ucrt64\bin"
+
+set build_type=Debug
+::set build_type=Release
+::set build_type=MinSizeRel
+::set build_type=RelWithDebInfo
+
+set "repo_dir=%~dp0"
+:: Удаляем обратный слэш в конце
+set "repo_dir=%repo_dir:~0,-1%"
+
+:: Генерируем проект
+cmake "%repo_dir%" -B "%repo_dir%/../build_mingw" -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE=%build_type%
+
+:: Компилируем проект
+cmake --build "%repo_dir%/../build_mingw"
+
+:: Ждём нажатие Enter перед закрытием консоли
+pause
diff --git a/build_vs.bat b/build_vs.bat
new file mode 100644
index 0000000..f428c6d
--- /dev/null
+++ b/build_vs.bat
@@ -0,0 +1,25 @@
+:: Меняем кодировку консоли на UTF-8
+chcp 65001
+
+:: Указываем путь к cmake.exe. Без system32 в PATH ссылки на папки не создаются
+set "PATH=%SystemRoot%\system32;c:\programs\cmake\bin"
+
+set build_type=Debug
+::set build_type=Release
+::set build_type=MinSizeRel
+::set build_type=RelWithDebInfo
+
+set "generator=Visual Studio 17"
+
+set "repo_dir=%~dp0"
+:: Удаляем обратный слэш в конце
+set "repo_dir=%repo_dir:~0,-1%"
+
+:: Генерируем проект
+cmake "%repo_dir%" -B "%repo_dir%/../build_vs" -G "%generator%" -A x64
+
+:: Компилируем проект
+cmake --build "%repo_dir%/../build_vs" --config %build_type%
+
+:: Ждём нажатие Enter перед закрытием консоли
+pause
diff --git a/docs/1_basics.md b/docs/1_basics.md
new file mode 100644
index 0000000..b353544
--- /dev/null
+++ b/docs/1_basics.md
@@ -0,0 +1,134 @@
+**Главы:**
+1) [Основы длинной арифметики](1_basics.md)
+2) [Длина неполного частного](2_div_len.md)
+3) [Деление путём подбора разрядов](3_brute_force_div.md)
+4) [Деление столбиком](4_long_div.md)
+
+----------------------------------------------
+
+# 1. Основы длинной арифметики
+
+Основание (base) системы счисления (СС) — число цифр:
+* в десятичной СС 10 цифр: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
+* в двоичной — две: 0, 1
+* в шестнадцатеричной — 16: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
+* и т.д.
+
+В позиционной СС значение каждой цифры зависит от позиции (разряда).
+Например в числе 472 четыре сотни, семь десятков и две единицы: `4·100 + 7·10 + 2·1`.
+
+Разряды нумеруются с конца. Разряды слева — старшие, а справа — младшие.
+Номер разряда совпадает со степенью base: 4·102 + 7·101 + 2·100
.
+
+Большие числа можно хранить в виде массивов цифр (по одной цифре в каждом элементе массива).
+При этом математические операции придётся реализовать самостоятельно (например столбиком, как в школе).
+
+Литература:
+1. https://e-maxx.ru/algo/big_integer (ещё в разделе [bookz](http://e-maxx.ru/bookz/) есть полезные Кнут и Окулов)
+2. https://ru.wikipedia.org/wiki/Длинная_арифметика
+3. https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
+4. https://ru.wikipedia.org/wiki/Позиционная_система_счисления
+
+***Внимание! Уже упомянутая литература в последующих темах не упоминается.***
+
+## Сложение столбиком и реверс цифр
+
+Пример:
+
+```
+ 238
++ 932
+ ────
+ 1170
+```
+
+1) Складываем младшие разряды (единицы): `8 + 2 = 10`. Так как `10 >= base (10)`,
+ то есть перенос в следующий столбец
+2) Складываем разряды десятков: `3 + 3 + 1 = 7`. Переноса в столбец сотен нет, так как `7 < 10`
+3) Складываем разряды сотен: `2 + 9 + 0 = 11`. Так как `11 >= 10`, то есть перенос в столбец тысяч
+
+Реализация этого алгоритма: [1_basics_add_1.cpp](1_basics_add_1.cpp).
+
+***Внимание! В исходниках зачастую используется код из предыдущих программ, поэтому важно
+изучать их последовательно.***
+
+Тестировать код онлайн можно на сайтах:
+1)
+2)
+
+Опции для GCC: `-O0 -Wall -Wextra -Wpedantic -fsanitize=undefined -std=c++20`.
+
+В приведённом коде имеется 2 проблемы:
+1) Приходится добавлять цифры в начало массива
+2) Если числа разной длины, то приходится складывать цифры с разными индексами
+
+Эти проблемы легко обойти, если хранить цифры в обратном порядке.
+Реализация: [1_basics_add_2.cpp](1_basics_add_2.cpp).
+
+Литература:
+1. https://en.wikipedia.org/wiki/Carry_(arithmetic)
+2. [Гуровиц и Гольдштейн](https://informatics.msk.ru/course/view.php?id=17#section-5)
+3. https://www.geeksforgeeks.org/sum-two-large-numbers/
+
+## Умножение столбиком и смена base
+
+Рассмотрим программу [1_basics_mul_1.cpp](1_basics_mul_1.cpp), в которой реализовано умножение
+столбиком положительных десятичных чисел, а также ввод длинных чисел в виде строк.
+В каждом элементе массива (тип uint32_t) мы храним по одной десятичной цифре.
+Лучше будет использовать не десятичную СС, а СС с основанием 109 (в этой СС не 10 цифр, а 1'000'000'000 цифр).
+При этом массивы с цифрами будут короче, а значит будет меньше потребление памяти и быстрее вычисления.
+
+Почему выбрано именно base 109?
+1) Числа из СС с основанием 10x легко преобразовывать в десятичные строки и обратно
+ (одна цифра длинного числа содержит ровно x десятичных цифр)
+2) Тип uint32_t умещает 109 − 1
, но не умещает 1010 − 1
,
+ поэтому base 1010 уже не годится
+
+Таким образом, программа будет иметь вид: [1_basics_mul_2.cpp](1_basics_mul_2.cpp).
+Обратите внимание, что при промежуточных вычислениях
+используется тип uint64_t, который способен уместить значение base2 − 1
.
+
+Литература:
+1. https://en.wikipedia.org/wiki/Multiplication_algorithm#Other_notations
+2. http://cppalgo.blogspot.com/2010/05/blog-post.html
+3. https://www.geeksforgeeks.org/multiply-large-numbers-represented-as-strings/
+4. https://brestprog.by/topics/longarithmetics/
+
+## Вычитание столбиком
+
+Рассмотрим программу [1_basics_sub_1.cpp](1_basics_sub_1.cpp), в которой реализовано вычитание столбиком длинных чисел.
+В целом, алгоритм похож на сложение [1_basics_add_2.cpp](1_basics_add_2.cpp),
+только в нём происходит не перенос единицы в старший разряд, а заём единицы из старшего разряда.
+Так как цифры хранятся в типе uint32_t, то перед вычитанием цифр производится сравнение,
+чтобы результатом вычисления не стало отрицательное число (и переполнение).
+Для избавления от лишних ветвлений воспользуемся тем фактом, что в C++ при преобразовании
+bool в число всегда получается 0 или 1.
+
+```
+uint32_t borrow = minuend_digit < subtrahend_digit;
+assert(borrow == 0 || borrow == 1);
+```
+
+Таким образом, программа будет иметь следующий вид: [1_basics_sub_2.cpp](1_basics_sub_2.cpp).
+
+## Деление путём вычитаний и сравнение
+
+Самый простой и самый медленный способ деления: из числителя (делимого) вычитаем знаменатель (делитель)
+до тех пор, пока остаток не станет меньше знаменателя.
+
+Пример: `91 / 8 = 91 − 8 − 8 − 8 − 8 …`
+Восьмёрку удаётся вычесть 11 раз (значит **неполное частное** = 11) и остаётся 3, которая меньше 8.
+
+Таким образом, для данного алгоритма также потребуется операция сравнения.
+Реализация: [1_basics_div.cpp](1_basics_div.cpp). Обратите внимание, что при сравнении очень важно,
+чтобы у чисел не было ведущих нулей.
+
+Недостатоком алгоритма является то, что если числитель большой, а знаменатель маленький, то число циклов будет огромным.
+
+Литература:
+1. https://ru.wikipedia.org/wiki/Алгоритм_деления#Деление_путём_вычитаний
+2. https://en.wikipedia.org/wiki/Division_algorithm
+
+----------------------------------------------
+
+Следующая глава: [2. Длина неполного частного](2_div_len.md)
diff --git a/docs/1_basics_add_1.cpp b/docs/1_basics_add_1.cpp
new file mode 100644
index 0000000..4ad73da
--- /dev/null
+++ b/docs/1_basics_add_1.cpp
@@ -0,0 +1,83 @@
+// Сложение столбиком
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+// Отдельная цифра длинного числа
+using Digit = uint32_t;
+
+// Складывает два положительных длинных десятичных числа столбиком
+vector add(const vector& a, const vector& b)
+{
+ // Выясняем, какое число длиннее
+ const vector& long_num = a.size() >= b.size() ? a : b;
+ const vector& short_num = a.size() >= b.size() ? b : a;
+
+ vector ret; // Результат
+
+ // Насколько число short_num короче числа long_num
+ size_t len_diff = long_num.size() - short_num.size();
+
+ // Перенос в старший разряд (всегда 0 или 1)
+ Digit carry = 0;
+
+ // Цикл начинается справа (с младших разрядов)
+ for (size_t index_short = short_num.size() - 1; index_short != size_t(-1); --index_short)
+ {
+ // Разряды, которые нужно складывать, имеют разные индексы (из-за разной длины чисел)
+ size_t index_long = index_short + len_diff;
+
+ uint32_t sum = long_num[index_long] + short_num[index_short] + carry;
+ carry = sum / 10;
+ assert(carry == 0 || carry == 1);
+ ret.insert(ret.begin(), sum % 10); // Добавляем цифру слева
+ }
+
+ // Оставшиеся цифры более длинного числа
+ for (size_t index_long = len_diff - 1; index_long != size_t(-1); --index_long)
+ {
+ uint32_t sum = long_num[index_long] + carry;
+ carry = sum / 10;
+ assert(carry == 0 || carry == 1);
+ ret.insert(ret.begin(), sum % 10); // Добавляем цифру слева
+ }
+
+ if (carry)
+ ret.insert(ret.begin(), 1);
+
+ return ret;
+}
+
+// Преобразует длинное число в строку
+string to_string(const vector& big_num)
+{
+ string ret;
+
+ for (Digit digit : big_num)
+ ret += std::to_string(digit);
+
+ return ret;
+}
+
+// Печатает два длинных числа и их сумму
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " + " << to_string(b) << " = " << to_string(add(a, b)) << endl;
+}
+
+int main()
+{
+ show({1, 2, 3}, {4, 5, 6}); // 123 + 456 = 579
+ show({9, 9, 9}, {1, 2}); // 999 + 12 = 1011
+ show({1, 2}, {9, 9, 9}); // 12 + 999 = 1011
+ show({1, 2, 3}, {9, 9, 9}); // 123 + 999 = 1122
+ show({9, 9, 9, 9}, {9, 9, 9, 9}); // 9999 + 9999 = 19998
+
+ return 0;
+}
diff --git a/docs/1_basics_add_2.cpp b/docs/1_basics_add_2.cpp
new file mode 100644
index 0000000..a34dbb6
--- /dev/null
+++ b/docs/1_basics_add_2.cpp
@@ -0,0 +1,82 @@
+// Сложение столбиком: обратный порядок цифр
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+// Отдельная цифра длинного числа
+using Digit = uint32_t;
+
+// Складывает два положительных длинных десятичных числа столбиком.
+// Функция изменена по сравнению с 1_basics_add_1.cpp (теперь цифры в обратном порядке)
+vector add(const vector& a, const vector& b)
+{
+ // Выясняем, какое число длиннее
+ const vector& long_num = a.size() >= b.size() ? a : b;
+ const vector& short_num = a.size() >= b.size() ? b : a;
+
+ vector ret; // Результат
+
+ // Перенос в старший разряд (всегда 0 или 1)
+ Digit carry = 0;
+
+ // Цикл начинается слева (с младших разрядов)
+ for (size_t i = 0; i < short_num.size(); ++i)
+ {
+ uint32_t sum = long_num[i] + short_num[i] + carry;
+ carry = sum / 10;
+ assert(carry == 0 || carry == 1);
+ ret.push_back(sum % 10); // Добавляем цифру в конец
+ }
+
+ // Оставшиеся цифры более длинного числа
+ for (size_t i = short_num.size(); i < long_num.size(); ++i)
+ {
+ uint32_t sum = long_num[i] + carry;
+ carry = sum / 10;
+ assert(carry == 0 || carry == 1);
+ ret.push_back(sum % 10);
+ }
+
+ if (carry)
+ ret.push_back(1);
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция изменена по сравнению с 1_basics_add_1.cpp (теперь цифры в обратном порядке)
+string to_string(const vector& big_num)
+{
+ string ret;
+
+ // Цикл по цифрам числа в обратном порядке
+ for (Digit digit : big_num | views::reverse)
+ ret += std::to_string(digit);
+
+ return ret;
+}
+
+// Печатает два длинных числа и их сумму
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " + " << to_string(b) << " = " << to_string(add(a, b)) << endl;
+}
+
+// Функция изменена по сравнению с 1_basics_add_1.cpp (теперь цифры в обратном порядке)
+int main()
+{
+ show({3, 2, 1}, {6, 5, 4}); // 123 + 456 = 579
+ show({9, 9, 9}, {2, 1}); // 999 + 12 = 1011
+ show({2, 1}, {9, 9, 9}); // 12 + 999 = 1011
+ show({3, 2, 1}, {9, 9, 9}); // 123 + 999 = 1122
+ show({9, 9, 9, 9}, {9, 9, 9, 9}); // 9999 + 9999 = 19998
+
+ return 0;
+}
diff --git a/docs/1_basics_div.cpp b/docs/1_basics_div.cpp
new file mode 100644
index 0000000..eda85f3
--- /dev/null
+++ b/docs/1_basics_div.cpp
@@ -0,0 +1,219 @@
+// Деление путём вычитаний и сравнение
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+constexpr size_t chunk_length = 9;
+
+// Убеждаемся, что bool всегда преобразуется в 1 или 0
+static_assert(uint32_t(5 > 3) == 1 && uint32_t(bool(7)) == 1);
+
+// Складывает два положительных длинных числа столбиком.
+// Функция изменена по сравнению с 1_basics_add_2.cpp (base вместо 10)
+vector add(const vector& a, const vector& b)
+{
+ const vector& long_num = a.size() >= b.size() ? a : b;
+ const vector& short_num = a.size() >= b.size() ? b : a;
+
+ vector ret;
+
+ Digit carry = 0;
+
+ for (size_t i = 0; i < short_num.size(); ++i)
+ {
+ uint32_t sum = long_num[i] + short_num[i] + carry;
+ carry = sum / base;
+ assert(carry == 0 || carry == 1);
+ ret.push_back(sum % base);
+ }
+
+ for (size_t i = short_num.size(); i < long_num.size(); ++i)
+ {
+ uint32_t sum = long_num[i] + carry;
+ carry = sum / base;
+ assert(carry == 0 || carry == 1);
+ ret.push_back(sum % base);
+ }
+
+ if (carry)
+ ret.push_back(1);
+
+ return ret;
+}
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend.
+// Функция скопирована из 1_basics_sub_2.cpp
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ Digit borrow = 0;
+
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Определяет, меньше ли первое число
+bool first_is_less(const vector& first, const vector& second)
+{
+ // Проверяем, что нет ведущих нулей
+ assert(first.size() == 1 || first.back() != 0);
+ assert(second.size() == 1 || second.back() != 0);
+
+ // В числах не должно быть ведущих нулей
+ if (first.size() != second.size())
+ return first.size() < second.size(); // Если число короче, значит оно меньше
+
+ // Сравниваем разряды, начиная с конца (со старших разрядов)
+ for (size_t i = first.size() - 1; i != size_t(-1); --i)
+ {
+ if (first[i] != second[i])
+ return first[i] < second[i];
+ }
+
+ return false; // first == second
+}
+
+// Определяет, что длинное число равно нулю
+bool is_zero(const vector& number)
+{
+ return (number.size() == 1 && number[0] == 0);
+}
+
+// Возвращает неполное частное и остаток.
+// Если знаменатель == 0, возвращает {0, 0}
+pair, vector> div_mod(const vector& numerator, const vector& denominator)
+{
+ if (is_zero(denominator))
+ return {vector{0}, vector{0}};
+
+ vector quotient{0}; // Неполное частное
+ vector remainder = numerator; // Остаток
+
+ // Пока remainder >= denominator
+ while (!first_is_less(remainder, denominator))
+ {
+ quotient = add(quotient, {1}); // ++quotient
+ remainder = sub(remainder, denominator); // remainder -= denominator
+ }
+
+ return {quotient, remainder};
+}
+
+// Печатает два длинных числа и результат их деления
+void show(const vector& a, const vector& b)
+{
+ pair, vector> result = div_mod(a, b);
+
+ cout << to_string(a) << " / " << to_string(b) << " = " << to_string(result.first)
+ << " (remainder = " << to_string(result.second) << ")" << endl;
+}
+
+int main()
+{
+ show(to_num("0"), to_num("0")); // 0, 0
+ show(to_num("5"), to_num("3")); // 1, 2
+ show(to_num("123"), to_num("67")); // 1, 56
+ show(to_num("512"), to_num("17")); // 30, 2
+ show(to_num("999"), to_num("999")); // 1, 0
+ show(to_num("9999"), to_num("000")); // 0, 0
+
+ // 566, 994340
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 5000, 1111
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222222222222222222"));
+
+ // 130686, 5304519702476588520600956014
+ show(to_num("1293564533453453419234546456456456"), to_num("9898223443473294328742373747"));
+
+ return 0;
+}
diff --git a/docs/1_basics_mul_1.cpp b/docs/1_basics_mul_1.cpp
new file mode 100644
index 0000000..2af96ae
--- /dev/null
+++ b/docs/1_basics_mul_1.cpp
@@ -0,0 +1,119 @@
+// Умножение столбиком
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+// Отдельная цифра длинного числа
+using Digit = uint32_t;
+
+// Перемножает два положительных длинных десятичных числа столбиком
+vector mul(const vector& a, const vector& b)
+{
+ // В векторах должна быть хотя бы одна цифра
+ assert(a.size() > 0 && b.size() > 0);
+
+ vector ret; // Результат
+
+ // Максимальная длина результата = a.size() + b.size() (когда 999... * 999...),
+ // а минимальная - 1 (например при умножении на 0)
+ ret.resize(a.size() + b.size(), 0);
+
+ // Каждую цифру числа a умножаем на число b (начиная с младших разрядов)
+ for (size_t a_index = 0; a_index < a.size(); ++a_index)
+ {
+ Digit carry = 0; // Перенос в старший разряд
+ // Максимальный перенос при умножении двух цифр = 8 (при 9 * 9).
+ // Так как одновременно выполняется сложение, то может добавляться перенос от сложения (максимум 1).
+ // Таким образом carry может достигать 9 (например при 99 * 99)
+
+ for (size_t b_index = 0; b_index < b.size(); ++b_index)
+ {
+ size_t ret_index = a_index + b_index;
+
+ ret[ret_index] += a[a_index] * b[b_index] + carry;
+ assert(ret[ret_index] <= 9 + 9 * 9 + 9);
+ // Максимальное значение в каждой ячейке вектора =
+ // (base - 1) + (base - 1)^2 + (base - 1) = base^2 - 1
+ // (например при 999 * 999)
+
+ carry = ret[ret_index] / 10;
+ assert(carry <= 9);
+ ret[ret_index] %= 10;
+ }
+
+ assert(ret[a_index + b.size()] == 0);
+ ret[a_index + b.size()] = carry;
+ }
+
+ // Убираем ведущие нули
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_add_2.cpp
+string to_string(const vector& big_num)
+{
+ string ret;
+
+ for (Digit digit : big_num | views::reverse)
+ ret += std::to_string(digit);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ vector ret;
+
+ // Цикл по символам строки в обратном порядке
+ for (char c : str | views::reverse)
+ ret.push_back(Digit(c - '0'));
+
+ // Убираем ведущие нули
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Печатает два длинных числа и их произведение
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " * " << to_string(b) << " = " << to_string(mul(a, b)) << endl;
+}
+
+int main()
+{
+ show(to_num("2"), to_num("8")); // 16
+ show(to_num("123"), to_num("67")); // 8241
+ show(to_num("17"), to_num("512")); // 8704
+ show(to_num("99"), to_num("99")); // 9801
+ show(to_num("999"), to_num("999")); // 998001
+ show(to_num("000"), to_num("999")); // 0
+ show(to_num("3"), to_num("745")); // 2235
+
+ // 567005670000000
+ show(to_num("1000010"), to_num("567000000"));
+
+ // 1280399079067456714112168894673698375814428579386489438786544
+ show(to_num("1293564533453453419234546456456456"), to_num("989822344347329432874237374"));
+
+ // 99999999999999999999999999999989990000000000000000000000000000001
+ show(to_num("9999999999999999999999999999999999"), to_num("9999999999999999999999999999999"));
+
+ return 0;
+}
diff --git a/docs/1_basics_mul_2.cpp b/docs/1_basics_mul_2.cpp
new file mode 100644
index 0000000..816ed41
--- /dev/null
+++ b/docs/1_basics_mul_2.cpp
@@ -0,0 +1,147 @@
+// Умножение столбиком: смена base
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+// Основание системы счисления
+constexpr uint32_t base = 1'000'000'000;
+
+// Отдельная цифра длинного числа (0, 1, 2, ..., base-1)
+using Digit = uint32_t;
+
+// Тип удвоенной (double) длины. Умещает квадрат цифры
+using DDigit = uint64_t;
+
+// Число десятичных цифр в каждой цифре Digit при преобразовании в строку и обратно
+constexpr size_t chunk_length = 9;
+
+// Перемножает два положительных длинных числа столбиком.
+// Функция изменена по сравнению с 1_basics_mul_1.cpp (сменили base)
+vector mul(const vector& a, const vector& b)
+{
+ assert(a.size() > 0 && b.size() > 0);
+
+ vector ret;
+ ret.resize(a.size() + b.size(), 0);
+
+ for (size_t a_index = 0; a_index < a.size(); ++a_index)
+ {
+ Digit carry = 0;
+
+ for (size_t b_index = 0; b_index < b.size(); ++b_index)
+ {
+ size_t ret_index = a_index + b_index;
+
+ // uint32_t не может уместить base^2 - 1, а uint64_t - может
+ DDigit v = ret[ret_index] + (DDigit)a[a_index] * b[b_index] + carry;
+ assert(v <= (DDigit)base * base - 1);
+
+ carry = Digit(v / base);
+ assert(carry < base);
+ ret[ret_index] = v % base;
+ }
+
+ assert(ret[a_index + b.size()] == 0);
+ ret[a_index + b.size()] = carry;
+ }
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция изменена по сравнению с 1_basics_mul_1.cpp
+// (теперь одна цифра длинного числа соответствует девяти символам строки, а не одному)
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ // Первый кусок результата без ведущих нулей
+ string ret = std::to_string(big_num.back());
+
+ // Остальные куски результата с ведущими нулями
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция изменена по сравнению с 1_basics_mul_1.cpp
+// (теперь одна цифра длинного числа соответствует девяти символам строки, а не одному)
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length; // Число кусков в строке
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length) // Первый кусок полный
+ {
+ first_chunk_length = chunk_length;
+ }
+ else // Первый кусок неполный
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ // Извлекаем куски в обратном порядке и преобразуем в Digit (кроме первого куска)
+ for (size_t i = 0; i < num_chunks - 1; ++i) // i - номер куска с конца
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ // Преобразуем в Digit первый кусок
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ // Убираем ведущие нули
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Печатает два длинных числа и их произведение
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " * " << to_string(b) << " = " << to_string(mul(a, b)) << endl;
+}
+
+int main()
+{
+ show(to_num("2"), to_num("8")); // 16
+ show(to_num("123"), to_num("67")); // 8241
+ show(to_num("17"), to_num("512")); // 8704
+ show(to_num("99"), to_num("99")); // 9801
+ show(to_num("999"), to_num("999")); // 998001
+ show(to_num("000"), to_num("999")); // 0
+ show(to_num("3"), to_num("745")); // 2235
+
+ // 567005670000000
+ show(to_num("1000010"), to_num("567000000"));
+
+ // 1280399079067456714112168894673698375814428579386489438786544
+ show(to_num("1293564533453453419234546456456456"), to_num("989822344347329432874237374"));
+
+ // 99999999999999999999999999999989990000000000000000000000000000001
+ show(to_num("9999999999999999999999999999999999"), to_num("9999999999999999999999999999999"));
+
+ return 0;
+}
diff --git a/docs/1_basics_sub_1.cpp b/docs/1_basics_sub_1.cpp
new file mode 100644
index 0000000..81b7e4a
--- /dev/null
+++ b/docs/1_basics_sub_1.cpp
@@ -0,0 +1,153 @@
+// Вычитание столбиком
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+constexpr size_t chunk_length = 9;
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ // Заём из следующего столбца
+ Digit borrow = 0; // Всегда 0 или 1
+
+ // Вычитаем цифры чисел, начиная с младших разрядов
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+
+ // Если занимали на предыдущем шаге, то нужно вычесть на 1 больше
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+ borrow = 0;
+
+ // Если нужен заём на данном шаге
+ if (minuend_digit < subtrahend_digit)
+ {
+ // Занимаем из старшего разряда
+ minuend_digit += base;
+ borrow = 1;
+ }
+
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ // Оставшиеся цифры minuend
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = 0;
+
+ if (minuend_digit < subtrahend_digit)
+ {
+ minuend_digit += base;
+ borrow = 1;
+ }
+
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ // Убираем ведущие нули
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Печатает два длинных числа и их разность
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " - " << to_string(b) << " = " << to_string(sub(a, b)) << endl;
+}
+
+int main()
+{
+ show(to_num("0"), to_num("0")); // 0
+ show(to_num("5"), to_num("3")); // 2
+ show(to_num("123"), to_num("67")); // 56
+ show(to_num("512"), to_num("17")); // 495
+ show(to_num("999"), to_num("999")); // 0
+ show(to_num("9999"), to_num("000")); // 9999
+
+ // 565999990
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 11111111111111111108888888888888888889
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222"));
+
+ // 1293563543631109071905113582219082
+ show(to_num("1293564533453453419234546456456456"), to_num("989822344347329432874237374"));
+
+ // 9990000000000000000000000000000000
+ show(to_num("9999999999999999999999999999999999"), to_num("9999999999999999999999999999999"));
+
+ return 0;
+}
diff --git a/docs/1_basics_sub_2.cpp b/docs/1_basics_sub_2.cpp
new file mode 100644
index 0000000..28b7d86
--- /dev/null
+++ b/docs/1_basics_sub_2.cpp
@@ -0,0 +1,150 @@
+// Вычитание столбиком: убираем ветвления
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+constexpr size_t chunk_length = 9;
+
+// Убеждаемся, что bool всегда преобразуется в 1 или 0
+static_assert(uint32_t(5 > 3) == 1 && uint32_t(bool(7)) == 1);
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend.
+// Функция изменена по сравнению с 1_basics_sub_1.cpp (убраны ветвления)
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ // Заём из следующего столбца
+ Digit borrow = 0; // Всегда 0 или 1
+
+ // Вычитаем цифры чисел, начиная с младших разрядов
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+
+ // Если занимали на предыдущем шаге, то нужно вычесть на 1 больше
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+
+ // Определяем, нужен ли заём на данном шаге
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+
+ // Занимаем из старшего разряда, если нужно
+ minuend_digit += base * borrow;
+
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ // Оставшиеся цифры minuend
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ // Убираем ведущие нули
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Печатает два длинных числа и их разность
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " - " << to_string(b) << " = " << to_string(sub(a, b)) << endl;
+}
+
+int main()
+{
+ show(to_num("0"), to_num("0")); // 0
+ show(to_num("5"), to_num("3")); // 2
+ show(to_num("123"), to_num("67")); // 56
+ show(to_num("512"), to_num("17")); // 495
+ show(to_num("999"), to_num("999")); // 0
+ show(to_num("9999"), to_num("000")); // 9999
+
+ // 565999990
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 11111111111111111108888888888888888889
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222"));
+
+ // 1293563543631109071905113582219082
+ show(to_num("1293564533453453419234546456456456"), to_num("989822344347329432874237374"));
+
+ // 9990000000000000000000000000000000
+ show(to_num("9999999999999999999999999999999999"), to_num("9999999999999999999999999999999"));
+
+ return 0;
+}
diff --git a/docs/2_div_len.cpp b/docs/2_div_len.cpp
new file mode 100644
index 0000000..10c1e8a
--- /dev/null
+++ b/docs/2_div_len.cpp
@@ -0,0 +1,166 @@
+// Точная длина неполного частного
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+using Digit = uint32_t;
+
+#if true
+constexpr uint32_t base = 1'0;
+constexpr size_t chunk_length = 1;
+#elif true
+constexpr uint32_t base = 1'00;
+constexpr size_t chunk_length = 2;
+#elif true
+constexpr uint32_t base = 1'000'000'000;
+constexpr size_t chunk_length = 9;
+#endif
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Определяет, меньше ли первое число.
+// Функция скопирована из 1_basics_div.cpp
+bool first_is_less(const vector& first, const vector& second)
+{
+ assert(first.size() == 1 || first.back() != 0);
+ assert(second.size() == 1 || second.back() != 0);
+
+ if (first.size() != second.size())
+ return first.size() < second.size();
+
+ for (size_t i = first.size() - 1; i != size_t(-1); --i)
+ {
+ if (first[i] != second[i])
+ return first[i] < second[i];
+ }
+
+ return false;
+}
+
+// Определяет, что длинное число равно нулю.
+// Функция скопирована из 1_basics_div.cpp
+bool is_zero(const vector& number)
+{
+ return (number.size() == 1 && number[0] == 0);
+}
+
+// Определяет длину неполного частного
+size_t calc_quotient(const vector& numerator, const vector& denominator)
+{
+ if (is_zero(denominator))
+ return 1; // Пусть при делении на 0 результатом будет 0 (длина = 1)
+
+ if (first_is_less(numerator, denominator)) // Если числитель меньше знаменателя
+ return 1; // Неполное частное = 0 (длина = 1)
+
+ vector chunk; // Первое неполное делимое
+
+ // Ищем первое неполное делимое (начинаем со старших разрядов)
+ for (size_t i = numerator.size() - 1; i != size_t(-1); --i)
+ {
+ // Добавляем цифру слева (так как храним цифры в обратном порядке)
+ chunk.insert(chunk.begin(), numerator[i]);
+
+ if (!first_is_less(chunk, denominator)) // chunk >= denominator
+ break; // Нашли первое неполное делимое
+ }
+
+ // Неполное делимое максимум на одну цифру длиннее знаменателя
+ assert(chunk.size() == denominator.size() || chunk.size() == denominator.size() + 1);
+
+ size_t ret = numerator.size() - chunk.size() + 1;
+ assert(ret == numerator.size() - denominator.size() || ret == numerator.size() - denominator.size() + 1);
+
+ return ret;
+}
+
+// Печатает два длинных числа и длину неполного частного
+void show(const vector& a, const vector& b)
+{
+ cout << to_string(a) << " / " << to_string(b) << " | result length = " << calc_quotient(a, b) << endl;
+}
+
+int main()
+{
+ show(to_num("0"), to_num("0")); // 1 (0)
+ show(to_num("5"), to_num("3")); // 1 (1)
+ show(to_num("123"), to_num("67")); // 1 (1)
+ show(to_num("512"), to_num("17")); // 2 (30)
+ show(to_num("999"), to_num("999")); // 1 (1)
+ show(to_num("9999"), to_num("000")); // 1 (0)
+
+ // 3 (результат деления = 566)
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 4 (5000)
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222222222222222222"));
+
+ // 6 (130686)
+ show(to_num("1293564533453453419234546456456456"), to_num("9898223443473294328742373747"));
+
+ // 50 (13068658137053464352525468785867118980456379357846)
+ show(to_num("12935645334534534192345464564564563443473294328742373747"), to_num("989822"));
+
+ // 10 (3000000000)
+ show(to_num("15000000000"), to_num("5"));
+
+ return 0;
+}
diff --git a/docs/2_div_len.md b/docs/2_div_len.md
new file mode 100644
index 0000000..3497b1c
--- /dev/null
+++ b/docs/2_div_len.md
@@ -0,0 +1,64 @@
+**Главы:**
+1) [Основы длинной арифметики](1_basics.md)
+2) [Длина неполного частного](2_div_len.md)
+3) [Деление путём подбора разрядов](3_brute_force_div.md)
+4) [Деление столбиком](4_long_div.md)
+
+----------------------------------------------
+
+# 2. Длина неполного частного
+
+Длину результата деления можно узнать до выполнения деления.
+
+### Максимальная длина неполного частного
+
+1\) Результат деления будет максимально длинным, если в знаменателе 1:
+
+`989901 / 1 = 989901` ⇒ `длина результата = длина числителя = 6`
+
+2\) Результат будет максимально коротким, если знаменатель равен числителю:
+
+`989901 / 989901 = 1` ⇒ `длина результата = 1`
+
+3\) Ещё пример:
+
+`989901 / 99 = 9999` ⇒ `длина результата = длина числителя − длина знаменателя = 6 − 2 = 4`
+
+4\) Ещё пример (без учёта остатка):
+
+`989901 / 70 = 14141` ⇒ `длина результата = длина числителя − длина знаменателя + 1 = 6 − 2 + 1 = 5`
+
+Формула 4) подходит и для случаев 1) и 2), но не подходит для примера 3).
+Таким образом, эта формула определяет **максимальную** длину результата.
+
+### Точная длина неполного частного
+
+Допустим нам нужно разделить 10512 на 23.
+
+1\) Ищем первое неполное делимое ("неполное", потому что это фрагмент делимого):
+
+1. 1 не годится, так как меньше 23
+2. 10 не годится, так как меньше 23
+3. 105 - первое неполное делимое, так как >= 23
+
+2\) Первое неполное делимое даёт одну цифру результата. Плюс остальные цифры числителя дают по одной цифре результата:
+
+`длина результата = длина числителя - длина первого неполного делимого + 1 = 5 - 3 + 1 = 3`
+
+На самом деле при делении каждая цифра числителя даёт одну цифру результата
+(это будет понятно позже при рассмотрении деления столбиком), но ведущие нули
+отбрасываются, а нули внутри числа - нет: `11832 / 29 = 00408 = 408`.
+
+Реализация: [2_div_len.cpp](2_div_len.cpp).
+
+Обычно точно вычислять длину результата не требуется.
+Для выделения места достаточно формулы максимальной длины, которая
+может превысить реальную длину максимум на 1.
+
+Источники:
+1. https://www.youtube.com/watch?v=ioeAg9emsqE
+2. https://www.youtube.com/watch?v=p-CtgmGOqPs
+
+----------------------------------------------
+
+Следующая глава: [3. Деление путём подбора разрядов](3_brute_force_div.md)
diff --git a/docs/3_brute_force_div.md b/docs/3_brute_force_div.md
new file mode 100644
index 0000000..2087a80
--- /dev/null
+++ b/docs/3_brute_force_div.md
@@ -0,0 +1,87 @@
+**Главы:**
+1) [Основы длинной арифметики](1_basics.md)
+2) [Длина неполного частного](2_div_len.md)
+3) [Деление путём подбора разрядов](3_brute_force_div.md)
+4) [Деление столбиком](4_long_div.md)
+
+----------------------------------------------
+
+# 3. Деление путём подбора разрядов
+
+Рассмотрим ещё один очень простой алгоритм деления.
+
+Допустим нам нужно разделить 63 на 3.
+Для простоты будем использовать десятичную СС и обычный порядок цифр.
+
+1\) `Максимальная длина результата = 2 − 1 + 1 = 2`. Обнуляем массив результата:
+
+`результат = {0, 0}`
+
+2\) Увеличим старший разряд результата на 1:
+
+`результат = {1, 0} // 10`
+
+3\) Проверяем, что `результат * знаменатель <= числитель`:
+
+`{1, 0} * {3} <= {6, 3} // 30 <= 63`
+
+Условие выполняется.
+
+4\) Снова увеличим старший разряд на 1 и снова проверяем:
+
+```
+результат = {2, 0} // 20
+{2, 0} * {3} <= {6, 3} // 60 <= 63
+```
+Условие выполняется.
+
+5\) Снова увеличим старший разряд на 1 и снова проверяем:
+
+```
+результат = {3, 0} // 30
+{3, 0} * {3} <= {6, 3} // 90 <= 63
+```
+
+Условие не выполняется. Значит на предыдущем шаге был найден старший разряд результата.
+
+6\) Вычитаем из старшего разряда 1, чтобы вернуть предыдущее значение:
+
+`результат = {2, 0} // 20`
+
+7\) Старший разряд мы подобрали, теперь начинаем увеличивать на 1 следующий разряд:
+
+```
+результат = {2, 1} // 21
+{2, 1} * {3} <= {6, 3} // 63 <= 63
+```
+
+Условие выполняется.
+
+8\) Снова увеличиваем на 1 первый разряд (нумерация с 0):
+
+```
+результат = {2, 2} // 22
+{2, 2} * {3} <= {6, 3} // 66 <= 63
+```
+
+Условие не выполняется. Значит на предыдущем шаге был найден первый разряд результата.
+
+9\) Вычитаем из первого разряда единицу:
+
+`результат = {2, 1} // 21`
+
+Разрядов больше нет, ответ получен.
+
+Реализация: [3_brute_force_div_1.cpp](3_brute_force_div_1.cpp).
+
+Из-за перебора цифр этот алгоритм также очень медленный. В худшем варианте каждую цифру результата придётся проверять base раз.
+Подбор цифр можно значительно ускорить с помощью [двоичного поиска](https://ru.wikipedia.org/wiki/Двоичный_поиск).
+Релизация: [3_brute_force_div_2.cpp](3_brute_force_div_2.cpp).
+
+Проблемой остаётся то, что при каждой проверке длинные числа перемножаются целиком.
+
+Источник: .
+
+----------------------------------------------
+
+Следующая глава: [4. Деление столбиком](4_long_div.md)
diff --git a/docs/3_brute_force_div_1.cpp b/docs/3_brute_force_div_1.cpp
new file mode 100644
index 0000000..8bbcfb9
--- /dev/null
+++ b/docs/3_brute_force_div_1.cpp
@@ -0,0 +1,237 @@
+// Деление путём подбора разрядов
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+using DDigit = uint64_t;
+constexpr size_t chunk_length = 9;
+
+// Убеждаемся, что bool всегда преобразуется в 1 или 0
+static_assert(uint32_t(5 > 3) == 1 && uint32_t(bool(7)) == 1);
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend.
+// Функция скопирована из 1_basics_sub_2.cpp
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ Digit borrow = 0;
+
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Перемножает два положительных длинных числа столбиком.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector mul(const vector& a, const vector& b)
+{
+ assert(a.size() > 0 && b.size() > 0);
+
+ vector ret;
+ ret.resize(a.size() + b.size(), 0);
+
+ for (size_t a_index = 0; a_index < a.size(); ++a_index)
+ {
+ Digit carry = 0;
+
+ for (size_t b_index = 0; b_index < b.size(); ++b_index)
+ {
+ size_t ret_index = a_index + b_index;
+ DDigit v = ret[ret_index] + (DDigit)a[a_index] * b[b_index] + carry;
+ assert(v <= (DDigit)base * base - 1);
+ carry = Digit(v / base);
+ assert(carry < base);
+ ret[ret_index] = v % base;
+ }
+
+ assert(ret[a_index + b.size()] == 0);
+ ret[a_index + b.size()] = carry;
+ }
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Определяет, меньше ли первое число.
+// Функция скопирована из 1_basics_div.cpp
+bool first_is_less(const vector& first, const vector& second)
+{
+ assert(first.size() == 1 || first.back() != 0);
+ assert(second.size() == 1 || second.back() != 0);
+
+ if (first.size() != second.size())
+ return first.size() < second.size();
+
+ for (size_t i = first.size() - 1; i != size_t(-1); --i)
+ {
+ if (first[i] != second[i])
+ return first[i] < second[i];
+ }
+
+ return false;
+}
+
+// Определяет, что длинное число равно нулю.
+// Функция скопирована из 1_basics_div.cpp
+bool is_zero(const vector& number)
+{
+ return (number.size() == 1 && number[0] == 0);
+}
+
+// Возвращает неполное частное и остаток.
+// Если знаменатель == 0, возвращает {0, 0}.
+// Функция изменена по сравнению с 1_basics_div.cpp (подбираем разряды, а не вычитаем в цикле)
+pair, vector> div_mod(const vector& numerator, const vector& denominator)
+{
+ // Если числитель или знаменатель == 0
+ if (is_zero(numerator) || is_zero(denominator))
+ return {vector{0}, vector{0}};
+
+ // Если числитель меньше знаменателя
+ if (first_is_less(numerator, denominator))
+ return {vector{0}, numerator};
+
+ // Неполное частное
+ vector quotient;
+ quotient.resize(numerator.size() - denominator.size() + 1, 0);
+
+ // Подбираем разряды, начиная со старшего разряда (с конца)
+ for (size_t i = quotient.size() - 1; i != size_t(-1); --i)
+ {
+ // Пока quotient * denominator <= numerator
+ while (!first_is_less(numerator, mul(quotient, denominator))) // Ведущие нули убираются при умножении
+ ++quotient[i]; // Увеличиваем разряд
+
+ assert(quotient[i] > 0);
+ --quotient[i]; // Откатываем разряд на один шаг назад
+ }
+
+ // Убираем ведущие нули
+ while (quotient.size() > 1 && quotient.back() == 0)
+ quotient.pop_back();
+
+ // Остаток = numerator - quotient * denominator
+ vector remainder = sub(numerator, mul(quotient, denominator));
+
+ return {quotient, remainder};
+}
+
+// Печатает два длинных числа и результат их деления
+void show(const vector& a, const vector& b)
+{
+ pair, vector> result = div_mod(a, b);
+
+ cout << to_string(a) << " / " << to_string(b) << " = " << to_string(result.first)
+ << " (remainder = " << to_string(result.second) << ")" << endl;
+}
+
+int main()
+{
+ show(to_num("0"), to_num("0")); // 0, 0
+ show(to_num("5"), to_num("3")); // 1, 2
+ show(to_num("123"), to_num("67")); // 1, 56
+ show(to_num("512"), to_num("17")); // 30, 2
+ show(to_num("999"), to_num("999")); // 1, 0
+ show(to_num("9999"), to_num("000")); // 0, 0
+
+ // 566, 994340
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 5000, 1111
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222222222222222222"));
+
+ // 130686, 5304519702476588520600956014
+ show(to_num("1293564533453453419234546456456456"), to_num("9898223443473294328742373747"));
+
+ return 0;
+}
diff --git a/docs/3_brute_force_div_2.cpp b/docs/3_brute_force_div_2.cpp
new file mode 100644
index 0000000..984b990
--- /dev/null
+++ b/docs/3_brute_force_div_2.cpp
@@ -0,0 +1,259 @@
+// Деление путём подбора разрядов: двоичный поиск
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+using DDigit = uint64_t;
+constexpr size_t chunk_length = 9;
+
+// Убеждаемся, что bool всегда преобразуется в 1 или 0
+static_assert(uint32_t(5 > 3) == 1 && uint32_t(bool(7)) == 1);
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend.
+// Функция скопирована из 1_basics_sub_2.cpp
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ Digit borrow = 0;
+
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Перемножает два положительных длинных числа столбиком.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector mul(const vector& a, const vector& b)
+{
+ assert(a.size() > 0 && b.size() > 0);
+
+ vector ret;
+ ret.resize(a.size() + b.size(), 0);
+
+ for (size_t a_index = 0; a_index < a.size(); ++a_index)
+ {
+ Digit carry = 0;
+
+ for (size_t b_index = 0; b_index < b.size(); ++b_index)
+ {
+ size_t ret_index = a_index + b_index;
+ DDigit v = ret[ret_index] + (DDigit)a[a_index] * b[b_index] + carry;
+ assert(v <= (DDigit)base * base - 1);
+ carry = Digit(v / base);
+ assert(carry < base);
+ ret[ret_index] = v % base;
+ }
+
+ assert(ret[a_index + b.size()] == 0);
+ ret[a_index + b.size()] = carry;
+ }
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Определяет, меньше ли первое число.
+// Функция скопирована из 1_basics_div.cpp
+bool first_is_less(const vector& first, const vector& second)
+{
+ assert(first.size() == 1 || first.back() != 0);
+ assert(second.size() == 1 || second.back() != 0);
+
+ if (first.size() != second.size())
+ return first.size() < second.size();
+
+ for (size_t i = first.size() - 1; i != size_t(-1); --i)
+ {
+ if (first[i] != second[i])
+ return first[i] < second[i];
+ }
+
+ return false;
+}
+
+// Определяет, что длинное число равно нулю.
+// Функция скопирована из 1_basics_div.cpp
+bool is_zero(const vector& number)
+{
+ return (number.size() == 1 && number[0] == 0);
+}
+
+// Возвращает неполное частное и остаток.
+// Если знаменатель == 0, возвращает {0, 0}.
+// Функция изменена по сравнению с 3_brute_force_div_1.cpp (двоичный поиск вместо перебора)
+pair, vector> div_mod(const vector& numerator, const vector& denominator)
+{
+ // Если числитель или знаменатель == 0
+ if (is_zero(numerator) || is_zero(denominator))
+ return {vector{0}, vector{0}};
+
+ // Если числитель меньше знаменателя
+ if (first_is_less(numerator, denominator))
+ return {vector{0}, numerator};
+
+ // Неполное частное
+ vector quotient;
+ quotient.resize(numerator.size() - denominator.size() + 1, 0);
+
+ // Подбираем разряды, начиная со старшего разряда (с конца)
+ for (size_t i = quotient.size() - 1; i != size_t(-1); --i)
+ {
+ Digit left = 0; // Левая граница диапазона цифр
+ Digit right = base - 1; // Правая граница
+ Digit best_digit = 0; // Максимальная подходящая цифра
+
+ while (left <= right)
+ {
+ // Делим диапазон цифр на две части
+ Digit middle = left + (right - left) / 2; // (left + right) / 2, но без переполнения
+ quotient[i] = middle; // Будем проверять эту цифру
+
+ if (first_is_less(numerator, mul(quotient, denominator))) // Ведущие нули убираются при умножении
+ {
+ // Цифра больше, чем нужно. Отбрасываем правую половину диапазона
+ right = middle - 1;
+ }
+ else // quotient * denominator <= numerator
+ {
+ // Цифра нам подходит, но вдруг найдём цифру больше. Отбрасываем левую половину диапазона
+ best_digit = middle;
+ left = middle + 1;
+ }
+ }
+
+ quotient[i] = best_digit;
+ }
+
+ // Убираем ведущие нули
+ while (quotient.size() > 1 && quotient.back() == 0)
+ quotient.pop_back();
+
+ // Остаток = numerator - quotient * denominator
+ vector remainder = sub(numerator, mul(quotient, denominator));
+
+ return {quotient, remainder};
+}
+
+// Печатает два длинных числа и результат их деления
+void show(const vector& a, const vector& b)
+{
+ pair, vector> result = div_mod(a, b);
+
+ cout << to_string(a) << " / " << to_string(b) << " = " << to_string(result.first)
+ << " (remainder = " << to_string(result.second) << ")" << endl;
+}
+
+// Функция изменена по сравнению с 3_brute_force_div_1.cpp (добавлен пример с маленьким знаменателем)
+int main()
+{
+ show(to_num("0"), to_num("0")); // 0, 0
+ show(to_num("5"), to_num("3")); // 1, 2
+ show(to_num("123"), to_num("67")); // 1, 56
+ show(to_num("512"), to_num("17")); // 30, 2
+ show(to_num("999"), to_num("999")); // 1, 0
+ show(to_num("9999"), to_num("000")); // 0, 0
+
+ // 566, 994340
+ show(to_num("567000000"), to_num("1000010"));
+
+ // 5000, 1111
+ show(to_num("11111111111111111111111111111111111111"), to_num("2222222222222222222222222222222222"));
+
+ // 130686, 5304519702476588520600956014
+ show(to_num("1293564533453453419234546456456456"), to_num("9898223443473294328742373747"));
+
+ // 13068658137053464352525468785867118980456379357846, 530335
+ show(to_num("12935645334534534192345464564564563443473294328742373747"), to_num("989822"));
+
+ return 0;
+}
diff --git a/docs/4_long_div.md b/docs/4_long_div.md
new file mode 100644
index 0000000..33a7ae3
--- /dev/null
+++ b/docs/4_long_div.md
@@ -0,0 +1,185 @@
+**Главы:**
+1) [Основы длинной арифметики](1_basics.md)
+2) [Длина неполного частного](2_div_len.md)
+3) [Деление путём подбора разрядов](3_brute_force_div.md)
+4) [Деление столбиком](4_long_div.md)
+
+----------------------------------------------
+
+# 4. Деление столбиком
+
+Деление уголком позволяет делить числитель покусочно, а значит при проверках перемножаются более короткие числа.
+
+Допустим нам нужно разделить 10512 на 23.
+Для простоты будем использовать десятичную СС и обычный порядок цифр.
+
+1\) Инициализация переменных:
+
+```
+результат = {} // Пустой массив
+кусок = {}
+```
+
+2\) При сложении и умножении мы начинали с младших разрядов, а при делении начинаем со старших.
+Копируем очередную цифру числителя в кусок:
+
+```
+// Для краткости пишем 1234 вместо {1, 2, 3, 4}
+кусок = 1 // Дописали 1
+```
+
+Дописывание цифры - это то же самое, что и `кусок = кусок * base + цифра`.
+
+3\) Делим кусок на знаменатель (подбором) и получаем очередную цифру результата:
+
+```
+разряд = кусок / знаменатель = 1 / 23 = 0
+результат = 0 // Ведущие нули потом нужно убрать
+```
+
+4\) Что осталось от куска (`кусок = кусок % знаменатель`):
+
+```
+кусок = кусок - разряд * знаменатель = 1 - 0 * 23 = 1 // Кусок не поменялся
+```
+
+5\) Повторяем алгоритм, начиная с шага 2, пока не кончатся цифры числителя:
+
+```
+кусок = 10 // Дописали 0
+разряд = кусок / знаменатель = 10 / 23 = 0
+результат = 00 // Дописали 0
+кусок = кусок - разряд * знаменатель = 10 - 0 * 23 = 10 // Кусок не поменялся
+
+кусок = 105 // Дописали 5
+разряд = кусок / знаменатель = 105 / 23 = 4
+результат = 004 // Дописали 4
+кусок = кусок - разряд * знаменатель = 105 - 4 * 23 = 13
+
+кусок = 131 // Дописали 1
+разряд = кусок / знаменатель = 131 / 23 = 5
+результат = 0045 // Дописали 5
+кусок = кусок - разряд * знаменатель = 131 - 5 * 23 = 16
+
+кусок = 162 // Дописали 2
+разряд = кусок / знаменатель = 162 / 23 = 7
+результат = 00457 // Дописали 7
+кусок = кусок - разряд * знаменатель = 162 - 7 * 23 = 1
+```
+
+6\) В итоге кусок содержит остаток от деления:
+
+```
+остаток = кусок = 1
+результат = 457 // Убрали ведущие нули
+```
+
+Релизация: [4_long_div_1.cpp](4_long_div_1.cpp).
+
+## Оптмизируем деление куска на знаменатель
+
+Мы формировали кусок таким образом, чтобы деление `кусок / знаменатель` давало ровно одну цифру результата,
+то есть `кусок < знаменатель · base`. Это открывает ряд путей для оптимизации.
+
+Если `кусок < знаменателя`, то `кусок / знаменатель = 0` и оптимизировать тут нечего. Поэтому далее
+будем рассматривать ситуацию, когда `кусок ≥ знаменателя`.
+
+### Оптимизация 1
+
+Обратите внимание, что кусок максимум на одну цифру длиннее знаменателя. То есть,
+если в знаменателе одна цифра, то кусок состоит из максимум двух цифр,
+а значит умещается в регистре процессора. В этом случае можно использовать обычное
+процессорное деление и ничего подбирать не нужно.
+
+Релизация: [4_long_div_2.cpp](4_long_div_2.cpp).
+
+### Оптимизация 2
+
+В литературе считается, что `длина куска = длина знаменателя + 1` (если `длина куска = длине знаменателя`,
+то к куску дописывается ведущий 0).
+
+Существует теорема, что если поделить две старших цифры куска на старшую цифру знаменателя,
+то полученная `примерная_цифра` будет довольно близко к искомой цифре и не меньше неё: `примерная_цифра ≥ цифра`.
+Это позволяет при двоичном поиске в качестве правой границы использовать не `base - 1`, а меньшее число.
+
+Но и значительные отклонения тоже бывают. Пример:
+
+```
+кусок = 94
+знаменатель = 19
+примерная_цифра = 09 / 1 = 9
+цифра = 94 / 19 = 4
+ошибка = примерная_цифра - цифра = 5
+```
+
+Пример при base = 109:
+
+```
+кусок = 1873'135157604'149223893
+знаменатель = 3119'654553545
+примерная_цифра = 1873'135157604 / 3119 = 600556318
+цифра = 600430312
+ошибка = примерная_цифра - цифра = 126006
+```
+
+Следует учесть, что иногда `примерная_цифра` может получиться больше `base - 1`, тогда её нужно ограничить сверху.
+Пример:
+
+```
+кусок = 169
+знаменатель = 19
+примерная_цифра = 16 / 1 = 16
+примерная_цифра = min(примерная_цифра, 9) = 9
+цифра = 169/19 = 8
+ошибка = примерная_цифра - цифра = 1
+```
+
+Релизация: [4_long_div_3.cpp](4_long_div_3.cpp).
+
+### Оптимизация 3
+
+Ещё одна теорема утверждает, что если старшая цифра знаменателя ≥ `base / 2`, то `примерная_цифра`
+будет больше искомой цифры максимум на 2, т.е. возможны всего 3 варианта:
+
+1. `цифра = примерная_цифра`
+2. `цифра = примерная_цифра - 1`
+3. `цифра = примерная_цифра - 2`
+
+Это позволяет отказаться от двоичного поиска и вернуться к перебору цифр, так как потребуется максимум две проверки.
+
+Если старшая цифра знаменателя < `base / 2`, тогда перед делением нужно
+умножить числитель и знаменатель на коэффициент, который даст нам подходящий знаменатель.
+Это называется нормализацией.
+Искомое частное после нормализации дроби не изменится, так как
+`(числитель · коэффициент) / (знаменатель · коэффициент) = числитель / знаменатель`,
+а вот искомый остаток после всех вычислений нужно разделить на коэффициент.
+Так как коэффициент - это одна цифра, то у нас для деления на коэффициент уже есть функция `div_by_digit()`.
+
+Реализация: [4_long_div_4.cpp](4_long_div_4.cpp).
+
+### Оптимизация 4
+
+Предыдущую оптимизацию можно улучшить, дополнительно изучив ещё по одной цифре куска и знаменателя.
+Это приведёт к тому, что `примерная_цифра` почти всегда будет совпадать с `искомой_цифрой`.
+И лишь с вероятностью `2 / base` `примерная_цифра` будет больше `искомой_цифры` на 1.
+То есть чем больше основание СС, тем меньше вероятность промаха.
+
+Реализация: [4_long_div_5.cpp](4_long_div_5.cpp).
+
+Литература:
+1. https://ru.wikipedia.org/wiki/Деление_столбиком
+2. https://en.wikipedia.org/wiki/Long_division
+3. https://ru.wikipedia.org/wiki/Деление_с_остатком
+4. https://www.codeproject.com/Articles/1276311/Multiple-Precision-Arithmetic-Division-Algorithm
+5. http://algolist.manual.ru/maths/longnum.php
+6. https://www.geeksforgeeks.org/divide-large-number-represented-string/
+7. [Pope, Stein. Multiple Precision Arithmetic](https://dl.acm.org/doi/10.1145/367487.367499)
+8. [Krishnamurthy, Nandi. On the Normalization Requirement of Divisor in Divide-and-Correct Methods](https://dl.acm.org/doi/abs/10.1145/363848.363867)
+9. [Дональд Кнут. Искусство программирования. Том 2. Третье издание. Раздел 4.3.1](https://djvu.online/file/AK1GKM4qtVd6r#p=311),
+ а также [упражнения](https://djvu.online/file/AK1GKM4qtVd6r#p=323)
+ и [ответы на упражнения](https://djvu.online/file/AK1GKM4qtVd6r#p=685)
+10. [Кнут на английском](https://www.haio.ir/app/uploads/2022/01/The-art-of-computer-programming.-Vol.2.-Seminumerical-algorithms-by-Knuth-Donald-E-z-lib.org_.pdf#page=285) (перевод на русский — неточный)
+11. https://rsdn.org/forum/alg/2355320.hot
+12. [Hansen. Multiple-Length Division Revisited: A Tour of the Minefield](https://surface.syr.edu/eecs_techreports/166/)
+13. [Brent, Zimmermann. Modern Computer Arithmetic](https://maths-people.anu.edu.au/~brent/pub/pub226.html)
+14. [Verma. Implementing Basic Arithmetic for Large Integers: Division](https://mathsanew.com/articles_html/23/implementing_large_integers_division.html)
diff --git a/docs/4_long_div_1.cpp b/docs/4_long_div_1.cpp
new file mode 100644
index 0000000..8f3556e
--- /dev/null
+++ b/docs/4_long_div_1.cpp
@@ -0,0 +1,299 @@
+// Деление столбиком
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+
+constexpr uint32_t base = 1'000'000'000;
+using Digit = uint32_t;
+using DDigit = uint64_t;
+constexpr size_t chunk_length = 9;
+
+// Убеждаемся, что bool всегда преобразуется в 1 или 0
+static_assert(uint32_t(5 > 3) == 1 && uint32_t(bool(7)) == 1);
+
+// Вычитает два положительных длинных числа столбиком.
+// Уменьшаемое minuend должно быть >= вычитаемого subtrahend.
+// Функция скопирована из 1_basics_sub_2.cpp
+vector sub(const vector& minuend, const vector& subtrahend)
+{
+ vector ret;
+ ret.resize(minuend.size());
+
+ Digit borrow = 0;
+
+ for (size_t i = 0; i < subtrahend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = subtrahend[i] + borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ for (size_t i = subtrahend.size(); i < minuend.size(); ++i)
+ {
+ Digit minuend_digit = minuend[i];
+ Digit subtrahend_digit = borrow;
+ borrow = minuend_digit < subtrahend_digit;
+ assert(borrow == 0 || borrow == 1);
+ minuend_digit += base * borrow;
+ ret[i] = minuend_digit - subtrahend_digit;
+ }
+
+ assert(borrow == 0);
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Перемножает два положительных длинных числа столбиком.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector mul(const vector& a, const vector& b)
+{
+ assert(a.size() > 0 && b.size() > 0);
+
+ vector ret;
+ ret.resize(a.size() + b.size(), 0);
+
+ for (size_t a_index = 0; a_index < a.size(); ++a_index)
+ {
+ Digit carry = 0;
+
+ for (size_t b_index = 0; b_index < b.size(); ++b_index)
+ {
+ size_t ret_index = a_index + b_index;
+ DDigit v = ret[ret_index] + (DDigit)a[a_index] * b[b_index] + carry;
+ assert(v <= (DDigit)base * base - 1);
+ carry = Digit(v / base);
+ assert(carry < base);
+ ret[ret_index] = v % base;
+ }
+
+ assert(ret[a_index + b.size()] == 0);
+ ret[a_index + b.size()] = carry;
+ }
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Преобразует длинное число в строку.
+// Функция скопирована из 1_basics_mul_2.cpp
+string to_string(const vector& big_num)
+{
+ assert(big_num.size() > 0);
+
+ string ret = std::to_string(big_num.back());
+
+ for (size_t i = big_num.size() - 2; i != size_t(-1); --i)
+ ret += format("{0:0>{1}}", big_num[i], chunk_length);
+
+ return ret;
+}
+
+// Преобразует строку в длинное число.
+// Функция скопирована из 1_basics_mul_2.cpp
+vector to_num(const string& str)
+{
+ if (str.empty())
+ return vector{0};
+
+ size_t num_chunks = str.length() / chunk_length;
+ size_t rest_length = str.length() - num_chunks * chunk_length;
+ size_t first_chunk_length;
+
+ if (!rest_length)
+ {
+ first_chunk_length = chunk_length;
+ }
+ else
+ {
+ first_chunk_length = rest_length;
+ ++num_chunks;
+ }
+
+ vector ret;
+
+ for (size_t i = 0; i < num_chunks - 1; ++i)
+ {
+ size_t chunk_start = str.size() - (i + 1) * chunk_length;
+ string chunk = str.substr(chunk_start, chunk_length);
+ ret.push_back(stoi(chunk));
+ }
+
+ string first_chunk = str.substr(0, first_chunk_length);
+ ret.push_back(stoi(first_chunk));
+
+ while (ret.size() > 1 && ret.back() == 0)
+ ret.pop_back();
+
+ return ret;
+}
+
+// Определяет, меньше ли первое число.
+// Функция скопирована из 1_basics_div.cpp
+bool first_is_less(const vector& first, const vector& second)
+{
+ assert(first.size() == 1 || first.back() != 0);
+ assert(second.size() == 1 || second.back() != 0);
+
+ if (first.size() != second.size())
+ return first.size() < second.size();
+
+ for (size_t i = first.size() - 1; i != size_t(-1); --i)
+ {
+ if (first[i] != second[i])
+ return first[i] < second[i];
+ }
+
+ return false;
+}
+
+// Определяет, что длинное число равно нулю.
+// Функция скопирована из 1_basics_div.cpp
+bool is_zero(const vector& number)
+{
+ return (number.size() == 1 && number[0] == 0);
+}
+
+// Отбрасывает младший разряд
+vector div_by_base(const vector& number)
+{
+ if (number.size() == 1) // Число < base
+ return vector{0};
+
+ // Отбрасываем цифру слева
+ return vector(number.cbegin() + 1, number.cend());
+}
+
+// Делит кусок числителя на знаменатель (подбором).
+// Кусок и знаменатель таковы, что в результате всегда одна цифра.
+// Это фрагмент функции div_mod() из 3_brute_force_div_2.cpp
+Digit div_chunk(const vector& chunk, const vector& denominator)
+{
+ if (first_is_less(chunk, denominator))
+ return 0;
+
+ assert(chunk.size() == denominator.size() // Длина куска равна длине знаменателя
+ || (chunk.size() == denominator.size() + 1 // Или кусок длиннее знаменателя на 1 цифру
+ && first_is_less(div_by_base(chunk), denominator))); // И эта цифра не лишняя
+
+ Digit left = 0;
+ Digit right = base - 1;
+ Digit best_digit = 0;
+
+ while (left <= right)
+ {
+ Digit middle = left + (right - left) / 2;
+ vector digit{middle};
+
+ if (first_is_less(chunk, mul(digit, denominator)))
+ {
+ right = middle - 1;
+ }
+ else
+ {
+ best_digit = middle;
+ left = middle + 1;
+ }
+ }
+
+ return best_digit;
+}
+
+// Возвращает неполное частное и остаток.
+// Если знаменатель == 0, возвращает {0, 0}.
+// Функция изменена по сравнению с 3_brute_force_div_2.cpp (делим числитель уголком, то есть покусочно)
+pair, vector> div_mod(const vector& numerator, const vector& denominator)
+{
+ // Если числитель или знаменатель == 0
+ if (is_zero(numerator) || is_zero(denominator))
+ return {vector{0}, vector{0}};
+
+ // Если числитель меньше знаменателя
+ if (first_is_less(numerator, denominator))
+ return {vector{0}, numerator};
+
+ vector quotient; // Неполное частное
+ vector chunk; // Текущий кусок числителя (и одновременно остаток)
+
+ // Копируем цифры числителя, начиная с конца (со старших разрядов)
+ for (size_t i = numerator.size() - 1; i != size_t(-1); --i)
+ {
+ // Копируем очередную цифру числителя в начало куска (так как цифры хранятся в обратном порядке)
+ chunk.insert(chunk.begin(), numerator[i]); // chunk = chunk * base + numerator[i]
+
+ // Убираем ведущие нули (когда chunk поделился без остатка и стал равен 0, а потом добавили 0)
+ while (chunk.size() > 1 && chunk.back() == 0)
+ chunk.pop_back();
+
+ // Делим кусок на знаменатель и получаем очередную цифру результата
+ Digit digit = div_chunk(chunk, denominator);
+ // Цифра равна 0, когда кусок меньше знаменателя
+
+ // Добавляем цифру к результату
+ quotient.push_back(digit);
+
+ // кусок -= цифра * знаменатель
+ chunk = sub(chunk, mul(vector