План v2 по диагностике (квиз 14/32) + 4 новые задачи (hash, heap, dijkstra, subnet) + тренажёр 60 карточек

This commit is contained in:
Kodlo-chan
2026-09-23 17:15:36 +07:00
parent 16b211eeda
commit 165dd5619d
19 changed files with 1429 additions and 136 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
diag/solutions/
diag/submissions/
diag/dist/
diag/tasks/*/t_0*
diag/tasks/*/t_*
diag/tasks/07_epoll/solution
diag/tasks/09_gdb/crash_checked
diag/tasks/08_bash/big.log
+33 -21
View File
@@ -1,40 +1,52 @@
# ИУП: подготовка к собеседованию Eltex (C/C++, алгоритмы, Linux, сети)
Личный учебный план и диагностический пакет. Репозиторий обновляется по ходу недели.
Личный учебный план и пакет задач с автопроверкой. Обновляется по ходу недели.
## Что внутри
- `WEEK7.md` — **рабочий документ**: интенсив на 7 дней (D0 23.09 → D7 30.09), три блока
в дне (алгоритмы / системное / практика+разбор), у каждого дня контрольная точка.
- `WEEK7.md` — **рабочий документ**: интенсив на 7 дней (D0 23.09 → D7 30.09), пересобранный
по результату диагностики (квиз 14/32: алгоритмы 1/8, Linux 3/8, сети 3/8, C/C++ 7/8).
Каждый день: карточки 30 мин → алгоритмы 3 ч → системное 3 ч → разбор и код 1,5 ч.
- `PLAN.md` — полный трек M1–M10 на пост-офферный период (модули, артефакты, материалы).
- `CONTEXT.md` — вводные, разбор вакансии (Eltex, hh #136775547), статус лейна.
- `diag/` — диагностический пакет:
- `quiz.py` + `quiz.json` — 32 вопроса по 4 доменам (C/C++, алгоритмы, Linux, сети);
- `tasks/01..09` — 9 практических задач с автопроверкой (ASAN/UBSAN, python-драйверы);
- `grade.py` — прогон задач с автооценкой;
- `questions_export.txt` — вопросы квиза текстом (можно отвечать строкой в чате).
- `TRACKER.md` — дневник: что решено, где встал, сколько минут.
- `TRACKER.md` — дневник: что решено, где встал, сколько минут. Это вход для калибровки.
- `diag/` — пакет:
- `quiz.py` + `quiz.json` — 32 вопроса по 4 доменам;
- `tasks/01..09` — диагностика: биты, список, ring buffer, IPv4-заголовок, НВП, потоки,
epoll-сервер, bash по логу, разбор падения в gdb;
- `tasks/10..13` — добор под слабые домены: хеш-таблица с открытой адресацией, куча
(`heapify` O(n), top-K), Дейкстра, арифметика подсетей;
- `facts_drill.py` + `facts.json` — 60 карточек «числа и факты» (сложности, байты
заголовков, `/26 = 62`, TTL, порядок TCP-хендшейка, паддинг структур);
- `grade.py` — автопроверка задач (ASAN/UBSAN, таймауты, PASS/FAIL + полный лог);
- `questions_export.txt` — вопросы квиза текстом.
## Как начать
```bash
git clone https://git.kodlo.art/Kodlo/eduplan-cpp-eltex.git
cd eduplan-cpp-eltex/diag
python3 quiz.py # квиз, ответы — номер варианта
python3 grade.py # все 9 задач (с санитайзерами)
python3 grade.py 01 04 # выборочно
python3 quiz.py # квиз (или --answers "1-3 2-4 ...")
python3 grade.py # все 13 задач с санитайзерами
python3 grade.py 10 11 13 # выборочно
python3 grade.py --fast # без санитайзеров
python3 facts_drill.py # 20 карточек интерактивно
python3 facts_drill.py --all # все 60
```
Нужны `g++` (C++17), `gcc`, `python3`; для задачи 07 — только Linux-сокеты,
для 08 — bash/awk/sort. Никаких внешних зависимостей.
Нужны `g++` (C++17), `gcc`, `python3`; для `07_epoll` — Linux-сокеты, для `08_bash` —
bash/awk/sort. Внешних зависимостей нет. На Windows задачи 07 и 08 удобнее гонять в WSL.
## Правила честности
Эталонных решений в репозитории нет — они лежат только на машинке студии, чтобы
автопроверка не протухла. Задача 09 (`answer.txt`) — часть оценки: важен ход разбора.
Эталонных решений в репозитории нет (лежат только на машинке студии) — иначе автопроверка
превращается в шпаргалку. Требования по сложности, которые тест не ловит (например
`heapify` за O(n)), проверяются на разборе по коду. `answer.txt` в задаче 09 — обязательная
часть оценки.
## Как отдавать результаты
## Куда отдавать результаты
- `python3 grade.py --submit` — складывает `results_*.json` и свои файлы решений рядом;
- либо коммит в ветку `answers/<день>` и push (доступ на запись у аккаунта `tura`);
- либо файлами в чат / в топик лейна.
- ветка `answers/<день>` в этом репозитории;
- либо файлы/`results_*.json` в чат;
- либо `python3 grade.py --submit` — складывает результаты и решения рядом.
Ежедневный блок плана приходит в топик лейна в 09:00 (НСК).
+141 -112
View File
@@ -1,138 +1,167 @@
# ИУП — интенсив 7 дней (фуллтайм, собеседование ≤ 1 неделя)
Срок: собеседование максимум через неделю, подготовка — фуллтайм (~8 ч/день).
Цель интенсива: закрыть ровно то, что спросят на входе в Eltex (C/C++, базовые алгоритмы и
структуры данных, Linux, L2/L3-сети, инструменты), и не утонуть в глубине.
**Версия 2, 23.09** — пересобрана по результату диагностики (квиз 14/32).
Срок: собеседование максимум через неделю, подготовка фуллтайм (~8 ч/день).
Цель: закрыть ровно то, что спросят на входе в Eltex (C/C++, базовые алгоритмы и структуры
данных, Linux, L2/L3-сети, инструменты), и не утонуть в глубине.
## Что показала диагностика (это и определило план)
| Домен | Результат | Вывод |
|---|---|---|
| C/C++ | 7/8 | сильная сторона, **времени почти не даём** |
| Алгоритмы | **1/8** | провал: формальные свойства (сложности, условия применимости) |
| Linux | 3/8 | пропущены fork/COW, epoll, mmap, waitpid — «не был уверен» |
| Сети | 3/8 | пропущены числа и байты (14/4/62/TTL), порядок TCP-хендшейка |
Отдельно: из 32 вопросов **11 пропущены**, и это почти все «точные факты» — числа, байты,
сложности. То есть база есть, а не хватает заученных формулировок и уверенности в них.
Поэтому в плане появился ежедневный блок карточек (`facts_drill.py`), а C++ урезан:
за него отвечает сильная сторона, забирать у неё часы смысла нет.
## Правила интенсива
1. Каждый день — три блока: **утро (3 ч) алгоритмы**, **день (3 ч) системное/язык**,
**вечер (2 ч) практика + разбор**. Блок не начинается, пока не закрыта вчерашняя точка.
1. Каждый день — три блока: **карточки (30 мин) → алгоритмы (3 ч) → системное (3 ч) →
разбор и код (1,5 ч)**. Блок не начинается, пока не закрыта вчерашняя точка.
2. День закрыт, только если: решены задачи дня **без подсказки**, написан/починен код дня,
сделана **одна задача вслух на таймер** (10 мин: 2 мин на разбор + код + сложность).
3. Всё пишем, а не читаем: теория — только под конкретную задачу, максимум 40 минут.
4. Трекер: `/home/pi/eduplan/diag/submissions/` или файл у себя — что решено, где встал,
сколько минут. Это вход для калибровки на следующий день.
5. Что осознанно НЕ лезет в неделю: полноценное ядро (модуль+драйвер), электроника,
Docker-продакшн. Ядро — обзорно на D6, электроника — после оффера (см. `PLAN.md`).
6. Если день провален — не догоняем всё, а переносим хвост в D7 (день повтора) и режем
второстепенное (M8-обзор, docker).
сделана **одна задача вслух на таймер** (10 мин: разбор + код + сложность), заполнен
трекер.
3. Теория — только под конкретную задачу, максимум 40 минут. Всё остальное — код и разбор
своих ошибок по трекеру.
4. Требования по сложности, которые тест не ловит (например `heapify` за O(n)) — проверяю
глазами по коду при разборе. Если делаешь «на оценку» не то, что заявлено, увижу.
5. Что осознанно НЕ лезет в неделю: полноценное ядро (модуль+драйвер как продукт),
электроника и схемотехника, Docker в продакшне. Ядро — обзорно на D6, электроника —
после оффера (полный трек `PLAN.md`, модули M8–M9).
6. Провалил день — не догоняю всё, переношу хвост в D7 и режу второстепенное (ядро, docker).
Материалы: cppreference (C и C++), man7.org (разделы 2/3/7), LKMPG, docs.kernel.org,
OSTEP (главы про процессы/память), Beej's Guide to Network Programming, RFC 791/793,
Wireshark wiki. Задачи: Codeforces с фильтром по тегам и рейтингу 800–1400
(`https://codeforces.com/problemset?tags=two+pointers,800-1400` — менять тег),
Яндекс Тренировки по алгоритмам (бесплатные контесты с разборами,
`https://yandex.ru/yaintern/training/algorithm-training`), классика LeetCode — по названию
(ссылки на leetcode.com открываются только в браузере).
Материалы: cppreference (C и C++), man7.org (разделы 2/3/7), Beej's Guide to Network
Programming, RFC 791/793, Wireshark wiki, OSTEP (процессы/память), LKMPG, docs.kernel.org.
Задачи: Codeforces с фильтром по тегу и рейтингу 800–1400
(`https://codeforces.com/problemset?tags=two+pointers,800-1400` — тег менять),
Яндекс Тренировки по алгоритмам (`https://yandex.ru/yaintern/training/algorithm-training`),
классика LeetCode — по названию (ссылки на leetcode.com открываются только в браузере).
## Пакет задач (каталог `diag/`)
- `01..09` — **диагностика**: битовые операции, список, ring buffer, IPv4-заголовок, НВП,
потоки, epoll-сервер, bash по логу, разбор падения в gdb.
- `10_hash`, `11_heap`, `12_dijkstra`, `13_subnet` — **добор под слабые домены**: своя
хеш-таблица с открытой адресацией, куча (`heapify` за O(n), top-K), Дейкстра на
`priority_queue`, арифметика подсетей.
- `python3 grade.py` — все 13 с автопроверкой (ASAN/UBSAN); `python3 grade.py 10 13` —
выборочно; `--fast` — без санитайзеров.
- `python3 facts_drill.py` — 60 карточек «числа и факты» (algo 20, linux 14, net 14,
c_cpp 12). Маркеры: `>=80%` ок, `50–79%` добор, `<50%` дыра.
- Трекер дня: `TRACKER.md` в корне репозитория.
---
## D0 — 23.09, диагностика (3–4 ч)
## D0 — 23.09, диагностика (квиз пройден, 14/32)
- Квиз: `python3 quiz.py` (или ответы строкой в чат), 32 вопроса, 4 домена.
- Задачи: `python3 grade.py` — 9 задач с автопроверкой (ASAN/UBSAN).
- Разбор промахов вместе со мной, калибровка: какие дни усилить, что срезать.
- Квиз: **пройден** — 14/32, слабейшие домены: алгоритмы, Linux, сети (см. таблицу выше).
- Осталось: `cd diag && python3 grade.py 01 02 03 04 05 06 07 08 09` — 9 практических задач.
- Разбор промахов квиза вместе со мной, калибровка дней (уже сделана в этой версии плана).
- **Точка:** есть `results_quiz.json` и `results_tasks.json`, названы 3 слабейших темы.
## D1 — 24.09: массивы/строки + память в C + процессы Linux
## D1 — 24.09: инварианты сложности + хеш-таблицы | fork/COW/сигналы
- Утро (3 ч) — алгоритмы: два указателя, скользящее окно, префиксные суммы, бинарный
поиск и `lower_bound` руками. Задачи: Two Sum, Valid Palindrome, Container With Most
Water, Longest Substring Without Repeating Characters, Binary Search + 6–8 задач с
Codeforces по тегам `two pointers` и `binary search`, рейтинг 800–1400.
- День (3 ч) — C и память: указатели и арифметика, выравнивание и padding, UB
(signed overflow, strict aliasing, неинициализированная память), malloc/free,
переполнение буфера; сборка с `-Wall -Wextra -Werror -fsanitize=address,undefined`.
Практика: переписать `tasks/01_bits` и `tasks/03_ring` на чистом C (без C++-обёрток),
прогнать под санитайзерами.
- Вечер (2 ч) — Linux: `fork/exec/wait`, зомби, сигналы, коды возврата. Практика: свой
мини-шелл (fork + exec + wait, обработка `exit`) — это прямой вопрос на собеседовании.
- **Точка:** 10+ задач решены; мини-шелл запускает `ls`/`cat`; ASAN чистый; лог дня.
- Карточки (30 мин) — домен `algo`: бинарный поиск в 1e6, хеш O(1)/worst O(n), heapify O(n),
устойчивость сортировок, Дейкстра и условие применимости.
- Утро, алгоритмы (3 ч) — **формальные свойства, а не «умение решать»**: O-оценки и
амортизация, хеш-таблицы (цепочки vs открытая адресация, загрузка, tombstone, worst O(n)),
сортировки и устойчивость, бинарный поиск и `lower_bound` руками.
Практика: **`tasks/10_hash`** — своя таблица на линейном зондировании; 6–8 задач
Codeforces по тегам `hashing` и `sortings`, рейтинг 800–1400.
- День, системное (3 ч) — Linux: `fork/exec/wait`, **COW-страницы**, зомби и `waitpid`,
сигналы и коды возврата (137 = 128+9 SIGKILL, 139 = 128+11 SIGSEGV), `errno`.
Практика: мини-шелл (fork + exec + wait + `exit` + пайплайн по желанию) — прямой вопрос
на собеседовании; прогнать под ASAN.
- Разбор и код (1,5 ч) — `tasks/01_bits` и `tasks/03_ring` **на чистом C** (без C++-обёрток),
сборка `-Wall -Wextra -Werror -fsanitize=address,undefined`; одна задача вслух; трекер.
- **Точка:** 10_hash PASS; мини-шелл запускает `ls`/`cat`; ASAN чистый; карточки `algo` ≥ 80%.
## D2 — 25.09: хеш-таблицы и сортировки + C++ (RAII, владение, STL) + git/Makefile
## D2 — 25.09: деревья и куча | epoll и неблокирующие сокеты
- Утро (3 ч) — алгоритмы: хеш-таблицы и множества (коллизии, открытая адресация vs
цепочки), сортировки и компараторы, stability. Задачи: Group Anagrams, Top K Frequent
Elements, Two Sum II, Merge Intervals, Sort Colors + 6–8 задач с тегами `hashing`,
`sortings`.
- День (3 ч) — C++: RAII, правило 0/3/5, move-семантика и `std::move`, умные указатели
(unique/shared/weak, циклы ссылок), контейнеры STL и итераторы, лямбды, исключения и
гарантии безопасности. Практика: свой `unique_ptr` и свой `vector` (упрощённый), класс-
владелец с правилом пяти; сборка с `-Werror`.
- Вечер (2 ч) — git (ветки, rebase, конфликты, `bisect`) и сборка: Makefile руками, затем
CMake для своих задач. Практика: репозиторий с 3 задачами, где сборка/тесты — одной
командой.
- **Точка:** мини-библиотека собирается с `-Werror`; git-сценарии (rebase, bisect) пройдены
руками; 10+ задач.
## D3 — 26.09: списки/стек + многопоточность + gdb
- Утро (3 ч) — алгоритмы: связные списки (разворот, цикл Флойда, k-й с конца), стек/
очередь/ring buffer, монотонный стек. Задачи: Reverse Linked List, Merge Two Sorted
Lists, Linked List Cycle, Valid Parentheses, Daily Temperatures + 6–8 задач с тегами
`linked list`, `stack`.
- День (3 ч) — многопоточность: `std::thread`, mutex/`lock_guard`, `condition_variable`
и predicate-цикл, атомики и `memory_order`, гонки, дедлоки, ложные пробуждения;
паттерны producer/consumer и пул потоков. Практика: довести `tasks/06_threads` до
продакшн-вида + написать пул потоков. TSan на этой машине недоступен (VMA) — проверяем
инвариантами + ASAN, в логе это помечено.
- Вечер (2 ч) — gdb: breakpoints, `watch`, `bt`, `frame`, отладка потоков, разбор
падения. Практика: `tasks/09_gdb` + своё падение (переполнение буфера) с письменным
разбором.
- **Точка:** 10+ задач; пул потоков проходит счётчики 4×4×20k; падение разобрано в gdb
письменно.
## D4 — 27.09: деревья/куча + Linux-системное (epoll) + L2/L3
- Утро (3 ч) — алгоритмы: бинарные деревья и BST, обходы (рекурсия и итерация), куча и
`priority_queue`, heapify за O(n), top-K, LCA. Задачи: Invert Binary Tree, Validate
Binary Search Tree, Binary Tree Level Order Traversal, Kth Largest Element, Lowest
Common Ancestor + 6–8 задач с тегами `trees`, `heaps`.
- День (3 ч) — Linux: файловый ввод-вывод (`open/read/write/lseek`), `mmap`,
- Карточки (30 мин) — `algo`: heap insert O(log n), top-K, обходы деревьев, сложность DFS
по памяти.
- Утро, алгоритмы (3 ч) — бинарные деревья и BST: обходы рекурсией и итерацией,
валидация BST, LCA; куча: **`heapify` снизу вверх за O(n)**, `priority_queue`, top-K,
`nth_element`. Практика: **`tasks/11_heap`** + задачи Invert Binary Tree, Validate BST,
Kth Largest Element, Top K Frequent Elements.
- День, системное (3 ч) — Linux: файловый ввод-вывод (`open/read/write/lseek`), **`mmap`**,
мультиплексирование `select → poll → epoll` (LT/ET), неблокирующие сокеты,
`SO_REUSEADDR`, обработка `EAGAIN/EINTR`. Практика: `tasks/07_epoll` — эхо-сервер
довести до вида «не падает под нагрузкой» + клиент, нагрузочный тест своими руками.
- Вечер (2 ч) — сети L2/L3: OSI и TCP/IP, инкапсуляция, Ethernet (14 байт заголовка),
MAC, ARP, VLAN 802.1Q, IPv4-заголовок и контрольная сумма, маски и подсети, ICMP.
Практика: `tasks/04_ipv4` + два письменных разбора дампов (`tcpdump`: ARP-обмен и
TCP-handshake).
- **Точка:** сервер держит 3+ одновременных соединения и не течёт по дескрипторам;
2 разбора дампов письменно; 10+ задач.
`SO_REUSEADDR`, `EAGAIN`/`EINTR`. Практика: **`tasks/07_epoll`** — эхо-сервер держит
нагрузку, плюс нагрузочный тест своими руками.
- Разбор и код (1,5 ч) — C++ **ровно по промахам**: выравнивание и padding (sizeof 12),
правило 0/3/5, virtual-деструктор, `std::move` как каст, UB — 45 минут, не больше;
одна задача вслух; трекер.
- **Точка:** 11_heap и 07_epoll PASS; сервер держит 3+ соединений и не течёт по дескрипторам;
карточки `c_cpp` = 100%.
## D5 — 28.09: графы + TCP глубоко + bash/proc
## D3 — 26.09: списки/стек | многопоточность и gdb
- Утро (3 ч) — алгоритмы: представления графа, BFS/DFS, топологическая сортировка,
компоненты связности, Дейкстра (неотрицательные веса), union-find. Задачи: Number of
Islands, Course Schedule, Clone Graph, Network Delay Time, Redundant Connection + 6–8
задач с тегами `graphs`, `dfs and similar`, `dsu`.
- День (3 ч) — сети глубже: TCP-автомат состояний, окно и подтверждения, ретрансмиссии и
таймеры, закрытие соединения, UDP, порты, NAT, DNS/DHCP минимум; работа с
- Карточки (30 мин) — `linux`: epoll O(1) против select O(n), mmap, waitpid, 137/139.
- Утро, алгоритмы (3 ч) — связные списки (разворот, **цикл Флойда**, k-й с конца),
стек/очередь/ring buffer, монотонный стек. Практика: `tasks/02_list`, `tasks/03_ring`
(если ещё не сданы) + Valid Parentheses, Daily Temperatures, Reverse Linked List.
- День, системное (3 ч) — многопоточность: `std::thread`, mutex/`lock_guard`,
`condition_variable` и predicate-цикл, атомики и `memory_order`, гонки, дедлоки,
ложные пробуждения; паттерны producer/consumer и пул потоков. Практика:
`tasks/06_threads` до продакшн-вида + свой пул потоков. (TSan на этой машине может
отвалиться по VMA — тогда инварианты + ASAN, в логе это помечено.)
- Разбор и код (1,5 ч) — gdb: breakpoints, `watch`, `bt`, `frame`, отладка потоков;
`tasks/09_gdb` + **своё падение** с письменным разбором; одна задача вслух; трекер.
- **Точка:** пул потоков проходит счётчики 4×4×20k; падение разобрано письменно;
карточки `linux` ≥ 80%.
## D4 — 27.09: графы | L2/L3 и подсети
- Карточки (30 мин) — `net`: 14 байт Ethernet, VLAN +4, /26 = 62, TTL, SYN/SYN-ACK/ACK.
- Утро, алгоритмы (3 ч) — графы: представления, BFS/DFS, топологическая сортировка
(только для DAG), компоненты связности, **Дейкстра (веса ≥ 0)**, union-find.
Практика: **`tasks/12_dijkstra`** + Number of Islands, Course Schedule, Network Delay
Time, Redundant Connection + 6–8 задач с тегами `graphs`, `dsu`.
- День, системное (3 ч) — сети L2/L3: OSI и TCP/IP, инкапсуляция, Ethernet (14 байт),
MAC, ARP, VLAN 802.1Q (+4 байта), IPv4-заголовок (20 байт) и контрольная сумма, **TTL**,
маски и подсети, ICMP. Практика: `tasks/04_ipv4` + **`tasks/13_subnet`** + два письменных
разбора дампов (`tcpdump`: ARP-обмен и TCP-handshake).
- Разбор и код (1,5 ч) — задача вслух (граф или подсеть); разбор промахов дня; трекер.
- **Точка:** 12_dijkstra и 13_subnet PASS; 2 разбора дампов письменно; карточки `net` ≥ 80%.
## D5 — 28.09: ДП | TCP глубоко, bash и /proc
- Карточки (30 мин) — `net` + `c_cpp` (20 байт IPv4/TCP, MTU 1500, DNS 53, ASAN/UBSAN).
- Утро, алгоритмы (3 ч) — ДП: состояния и переходы, рюкзак 0/1, **НВП за O(n log n)**,
строки (Edit Distance, Word Break). Практика: `tasks/05_lis` + Climbing Stairs,
House Robber, Coin Change, Edit Distance + 5–6 задач с тегом `dp`.
- День, системное (3 ч) — TCP глубоко: автомат состояний, окно и подтверждения,
ретрансмиссии и таймеры, закрытие, UDP, порты, NAT, DNS/DHCP минимум; работа с
`tcpdump`/Wireshark. Практика: 3 письменных разбора дампов (ретрансмиссия, ICMP
unreachable, DNS) + свой захват трафика своей же программой.
- Вечер (2 ч) — Linux-быт: `/proc`, `ulimit`, systemd-юниты обзорно; bash (пайплайны,
`set -euo pipefail`, awk/sed для логов). Практика: `tasks/08_bash` + свой скрипт
диагностики сети/логов.
- **Точка:** 8+ задач; 3 разбора дампов; скрипт работает на логе в 100k строк за секунды.
unreachable, DNS) + захват трафика своей программой.
- Разбор и код (1,5 ч) — bash и Linux-быт: пайплайны, `set -euo pipefail`, awk/sed,
`/proc`, `ulimit`; `tasks/08_bash` (лог в 1e6 строк — только awk/sort, не bash-цикл);
одна задача вслух; трекер.
- **Точка:** 05_lis и 08_bash PASS; 3 разбора дампов; скрипт держит лог 100k строк за секунды.
## D6 — 29.09: ДП + ядро/embedded обзорно + docker + резюме
## D6 — 29.09: закрытие слабых мест | ядро обзорно, docker, резюме
- Утро (3 ч) — алгоритмы: ДП — состояния и переходы, рюкзак, LIS за O(n log n), строковые
задачи. Задачи: Climbing Stairs, House Robber, Coin Change, Longest Increasing
Subsequence, Edit Distance, Word Break + 5–6 задач с тегом `dp`, рейтинг 800–1400.
- День (3 ч) — ядро и embedded обзорно: модуль ядра (сборка, insmod/rmmod, printk,
параметры, `/proc`), символьный драйвер (`file_operations`), device tree и cross-compile
— по касательной, ровно чтобы отвечать словами. Практика: hello-модуль + простейший
символьный драйвер (в QEMU, если на этой машине сборка не встанет).
- Вечер (2 ч) — docker (образ под сборку/тесты) и **резюме + сопроводительное под Eltex**
(учесть: рассматривают офисный формат).
- **Точка:** модуль грузится/выгружается; черновик резюме отревьюен.
- Карточки (30 мин) — все домены, полный прогон 60 карточек.
- Утро, алгоритмы (3 ч) — **только слабые места по трекеру**: добить `10..13` до PASS,
повторить hash/BST/heap/Дейкстра/ДП там, где были FAIL или «не знаю».
- День, системное (3 ч) — ядро и embedded **обзорно**: модуль (сборка, insmod/rmmod,
printk, параметры, `/proc`), символьный драйвер (`file_operations`), device tree,
cross-compile, uboot/bringup — ровно чтобы отвечать словами; docker (образ под
сборку/тесты) — 45 минут.
- Разбор и код (1,5 ч) — **резюме + сопроводительное под Eltex** (учесть офисный формат),
вопросы работодателю, легенда по опыту.
- **Точка:** все 13 задач PASS; карточки ≥ 80% во всех доменах; резюме готово к отправке.
## D7 — 30.09: повтор, два мок-интервью, выход
## D7 — 30.09: повтор и два мок-интервью
- 2 ч — утренний повтор: только слабые места из трекера (не всё подряд).
- 3 ч — мок-интервью №1: алгоритмы, 2 задачи вслух с таймером, я веду и разбираю.
- 2 ч — мок-интервью №2: C/C++ + Linux + сети (вопросы по кругу, включая разбор дампа).
- 2 ч — повтор: только карточки с промахами и слабые темы из трекера.
- 3 ч — мок-интервью №1: алгоритмы, 2 задачи вслух с таймером (я веду и разбираю).
- 2 ч — мок-интервью №2: C/C++ + Linux + сети, включая разбор дампа.
- 1 ч — вопросы работодателю, легенда по опыту, финальная вычитка резюме.
- **Точка:** ≤ 1 грубая ошибка на сессию; резюме и сопроводительное готовы.
@@ -142,4 +171,4 @@ Wireshark wiki. Задачи: Codeforces с фильтром по тегам и
Незакрытое (полный трек `PLAN.md`): M2 хвост (битовые/строковые алгоритмы, математика),
M6 глубина (STP, маршрутизация, HTTP), M7 полностью (Docker в бою, bisect на реальном
репозитории), M8 ядро (полноценный драйвер, uboot/bringup), M9 электроника.
репозитории), M8 ядро (полноценный драйвер, uboot/bringup), M9 электроника и схемотехника.
+29 -2
View File
@@ -21,6 +21,19 @@
| 08 | `08_bash` | разбор лога доступа: TOTAL/TOP-3/5XX, 110k строк |
| 09 | `09_gdb` | отладка ASAN-падения + письменный разбор в `answer.txt` |
Добор под слабые домены (добавлено 23.09 после диагностики — квиз 14/32):
| # | Задача | Что проверяется |
|---|--------|-----------------|
| 10 | `10_hash` | хеш-таблица с открытой адресацией: пробирование, перехеширование, erase |
| 11 | `11_heap` | куча: `heapify` снизу вверх O(n), `kth_largest`, `top_k` на 3M |
| 12 | `12_dijkstra` | Дейкстра на `priority_queue`: неотрицательные веса, 100k вершин |
| 13 | `13_subnet` | арифметика IPv4: сеть/broadcast/узлы, `/31`, `/32`, подбор префикса |
- `facts.json` + `facts_drill.py` — тренажёр «числа и факты»: 60 карточек
(algo 20, linux 14, net 14, c_cpp 12) под конкретные промахи квиза: сложности, байты
заголовков, `/26 = 62`, TTL, порядок SYN/SYN-ACK/ACK, паддинг структур, правило трёх/пяти.
Маркеры итога: `>=80%` ок, `50–79%` добор, `<50%` дыра.
- `grade.py` — прогон задач с автооценкой (`PASS/FAIL`, число проверок, время).
- `solutions/` — **эталонные решения, в руки не отдавать**: они лежат здесь, чтобы
автопроверка не «протухла» (эталон → все PASS, заглушка → FAIL).
@@ -35,9 +48,13 @@ python3 grade.py # все задачи (с ASAN/UBSAN/TSAN)
python3 grade.py 01 04 # только выбранные
python3 grade.py --fast # без санитайзеров, быстрее
python3 grade.py --submit # результаты + свои файлы в submissions/
python3 facts_drill.py # 20 карточек, интерактивно
python3 facts_drill.py --all # все 60
python3 facts_drill.py --domain algo # только алгоритмы
python3 facts_drill.py --answers "20|62|ttl|..." # проверить строку ответов разом
```
Порядок: сначала квиз, потом задачи с 01 по 09. Зависание = FAIL (есть таймауты).
Порядок: сначала квиз, потом задачи с 01 по 09 (диагностика), затем 10–13 как добор. Зависание = FAIL (есть таймауты).
Правила честности: без встроенных `popcount`/`reverse`-хелперов, без копирования чужих
решений, `answer.txt` в задаче 09 — обязательная часть (оценивается ход разбора).
@@ -45,10 +62,20 @@ python3 grade.py --submit # результаты + свои файлы в subm
- `results_quiz.json`: балл по каждому домену + разбор промахов с пояснениями.
- `results_tasks.json`: по задаче — PASS/FAIL, число проверок, время, полный лог сборки.
- Итог и решение по плану (с каких модулей стартовать) собираются по этим двум файлам.
- `results_facts.json`: тренажёр карточек — балл по доменам и список промахов.
- Итог и решение по плану (с каких модулей стартовать) собираются по этим файлам.
- Рабочий план недели: `WEEK7.md` в корне репозитория; трекер дня — `TRACKER.md`.
## Публикация и доступ
Репозиторий: https://git.kodlo.art/Kodlo/eduplan-cpp-eltex — клонируется без пароля,
обновляется по ходу недели. Результаты можно присылать веткой `answers/<день>`, файлами
в чат или через `grade.py --submit`.
Репозиторий: https://git.kodlo.art/Kodlo/eduplan-cpp-eltex (клонируется без пароля,
обновляется по ходу недели). Результаты можно присылать веткой `answers/<день>`,
файлами в чат или через `grade.py --submit`.
- Пакет в каталоге: `/home/pi/eduplan/diag` (мир читает; каталог доступен на исполнение).
- Инбокс для ответов: `/home/pi/eduplan/diag/submissions/` (0777).
- Архив для скачивания: `/home/pi/eduplan/diag/dist/eduplan_diag.tar.gz`.
+591
View File
@@ -0,0 +1,591 @@
{
"version": 1,
"note": "Карточки под промахи квиза 23.09: точные числа, сложности и условия.",
"facts": [
{
"domain": "algo",
"q": "Средняя сложность поиска в хеш-таблице при разумной загрузке?",
"a": "амортизированное O(1)",
"accept": [
"o(1)",
"амортизированное o(1)",
"o(1) амортизированное",
"амортизированно o(1)"
]
},
{
"domain": "algo",
"q": "Худшая сложность поиска в хеш-таблице (все ключи в одну корзину)?",
"a": "O(n)",
"accept": [
"o(n)"
]
},
{
"domain": "algo",
"q": "Сколько сравнений в худшем случае у бинарного поиска в массиве из 1 000 000 элементов?",
"a": "20",
"accept": [
"20",
"log2 1e6 = 20",
"около 20"
]
},
{
"domain": "algo",
"q": "При каком условии применим алгоритм Дейкстры?",
"a": "веса рёбер неотрицательны",
"accept": [
"неотрицательные веса",
"все веса >= 0",
"веса неотрицательны",
"нет отрицательных рёбер"
]
},
{
"domain": "algo",
"q": "Чем заменить Дейкстру при отрицательных рёбрах?",
"a": "Беллман–Форд",
"accept": [
"беллман-форд",
"беллман форд",
"bellman-ford"
]
},
{
"domain": "algo",
"q": "Сложность построения кучи (heapify) из произвольного массива?",
"a": "O(n)",
"accept": [
"o(n)",
"о(n)"
]
},
{
"domain": "algo",
"q": "Сложность вставки одного элемента в кучу?",
"a": "O(log n)",
"accept": [
"o(log n)"
]
},
{
"domain": "algo",
"q": "Какая сортировка устойчивая: merge, quick, heap или selection?",
"a": "merge sort",
"accept": [
"merge",
"merge sort",
"слиянием",
"сортировка слиянием"
]
},
{
"domain": "algo",
"q": "Худшая сложность quicksort?",
"a": "O(n²)",
"accept": [
"o(n^2)",
"o(n²)",
"o(n2)"
]
},
{
"domain": "algo",
"q": "Средняя сложность quicksort и mergesort?",
"a": "O(n log n)",
"accept": [
"o(n log n)",
"o(n*logn)",
"o(nlogn)"
]
},
{
"domain": "algo",
"q": "Для какого графа возможна топологическая сортировка?",
"a": "для ациклического (DAG)",
"accept": [
"dag",
"ациклический",
"без циклов",
"dag (ациклический)"
]
},
{
"domain": "algo",
"q": "Как найти цикл в связном списке за O(1) дополнительной памяти?",
"a": "алгоритм Флойда: черепаха и заяц",
"accept": [
"флойд",
"черепаха и заяц",
"алгоритм флойда"
]
},
{
"domain": "algo",
"q": "BFS или DFS даёт кратчайший путь в невзвешенном графе?",
"a": "BFS",
"accept": [
"bfs",
"обход в ширину"
]
},
{
"domain": "algo",
"q": "Сложность Дейкстры на priority_queue (двоичная куча)?",
"a": "O((V+E) log V)",
"accept": [
"o((v+e) log v)",
"o((v+e)logv)",
"o(e log v)"
]
},
{
"domain": "algo",
"q": "За какую сложность ищется НВП (LIS) в оптимальном решении?",
"a": "O(n log n)",
"accept": [
"o(n log n)",
"o(nlogn)",
"o(n*log n)"
]
},
{
"domain": "algo",
"q": "Сложность задачи о рюкзаке 0/1 через ДП по вместимости?",
"a": "O(n * W)",
"accept": [
"o(n*w)",
"o(nw)",
"o(n*w)"
]
},
{
"domain": "algo",
"q": "Насколько быстро работают операции union-find с эвристиками?",
"a": "почти O(1), O(α(n))",
"accept": [
"почти o(1)",
"o(alpha(n))",
"o(α(n))",
"обратная функция аккермана"
]
},
{
"domain": "algo",
"q": "Какова сложность по памяти рекурсивного DFS в графе в худшем случае?",
"a": "O(V) — глубина стека рекурсии",
"accept": [
"o(v)",
"o(h)",
"o(v) в худшем случае"
]
},
{
"domain": "algo",
"q": "Какова нижняя оценка сложности сортировки сравнениями?",
"a": "O(n log n)",
"accept": [
"o(n log n)",
"o(nlogn)"
]
},
{
"domain": "algo",
"q": "Структура для поиска слов по префиксу за O(len)?",
"a": "trie (префиксное дерево)",
"accept": [
"trie",
"префиксное дерево",
"бор"
]
},
{
"domain": "linux",
"q": "Что делает fork()?",
"a": "создаёт новый процесс (свой PID, COW-копия памяти)",
"accept": [
"создаёт новый процесс",
"создает новый процесс",
"новый процесс"
]
},
{
"domain": "linux",
"q": "Что происходит со страницами памяти при fork()?",
"a": "copy-on-write: копируются при первой записи",
"accept": [
"cow",
"copy-on-write",
"копирование при записи"
]
},
{
"domain": "linux",
"q": "Какой механизм держит десятки тысяч соединений в одном потоке?",
"a": "epoll",
"accept": [
"epoll",
"эполл"
]
},
{
"domain": "linux",
"q": "Сложность select против epoll на большом числе дескрипторов?",
"a": "select — O(n) по всем, epoll — O(1) на готовое событие",
"accept": [
"select o(n), epoll o(1)",
"epoll o(1)",
"o(1) на готовое"
]
},
{
"domain": "linux",
"q": "Что даёт mmap() на файл?",
"a": "отображает файл в память; страницы подгружаются по обращению",
"accept": [
"отображает файл в память",
"mmap файла в память",
"отображение файла в память"
]
},
{
"domain": "linux",
"q": "Зачем родителю waitpid()?",
"a": "чтобы забрать код возврата и не оставлять зомби",
"accept": [
"забрать код возврата",
"убрать зомби",
"чтобы не остались зомби"
]
},
{
"domain": "linux",
"q": "Что такое процесс-зомби?",
"a": "завершившийся процесс, чья запись ждёт wait/waitpid",
"accept": [
"завершившийся процесс без wait",
"ждёт wait",
"запись ждёт wait"
]
},
{
"domain": "linux",
"q": "Код возврата 137 — что это?",
"a": "SIGKILL (128+9) — убит",
"accept": [
"sigkill",
"128+9",
"убит sigkill"
]
},
{
"domain": "linux",
"q": "Код возврата 139 — что это?",
"a": "SIGSEGV (128+11) — падение по памяти",
"accept": [
"sigsegv",
"128+11",
"сегфолт"
]
},
{
"domain": "linux",
"q": "Что означает errno EAGAIN на неблокирующем сокете?",
"a": "данных сейчас нет, повторить позже",
"accept": [
"повторить позже",
"нет данных, попробовать снова",
"данных нет"
]
},
{
"domain": "linux",
"q": "Что такое TID (Thread ID) в /proc?",
"a": "номер задачи в /proc/<pid>/task/",
"accept": [
"номер задачи",
"task",
"в /proc/<pid>/task"
]
},
{
"domain": "linux",
"q": "Что делает ulimit -n?",
"a": "показывает/меняет лимит открытых файловых дескрипторов",
"accept": [
"лимит дескрипторов",
"лимит открытых файлов",
"число открытых файлов"
]
},
{
"domain": "linux",
"q": "Сигнал с номером 15?",
"a": "SIGTERM — запрос на завершение",
"accept": [
"sigterm",
"sigterm (мягкое завершение)",
"запрос на завершение"
]
},
{
"domain": "linux",
"q": "Сколько байт в заголовке TCP без опций?",
"a": "20",
"accept": [
"20",
"20 байт"
]
},
{
"domain": "net",
"q": "Сколько байт в Ethernet-заголовке без VLAN?",
"a": "14",
"accept": [
"14",
"14 байт"
]
},
{
"domain": "net",
"q": "Сколько байт добавляет VLAN-тег 802.1Q?",
"a": "4",
"accept": [
"4",
"4 байта"
]
},
{
"domain": "net",
"q": "Сколько адресов узлов в подсети /26?",
"a": "62",
"accept": [
"62",
"62 узла"
]
},
{
"domain": "net",
"q": "Сколько адресов узлов в подсети /24?",
"a": "254",
"accept": [
"254"
]
},
{
"domain": "net",
"q": "Какое поле IPv4-заголовка уменьшается на 1 на каждом маршрутизаторе?",
"a": "TTL",
"accept": [
"ttl"
]
},
{
"domain": "net",
"q": "Правильная последовательность установки TCP-соединения?",
"a": "SYN -> SYN-ACK -> ACK",
"accept": [
"syn, syn-ack, ack",
"syn syn-ack ack",
"syn -> syn-ack -> ack"
]
},
{
"domain": "net",
"q": "Что делает ARP?",
"a": "находит MAC по IP в локальном сегменте",
"accept": [
"mac по ip",
"ip -> mac",
"находит mac по ip"
]
},
{
"domain": "net",
"q": "Размер MTU Ethernet по умолчанию?",
"a": "1500 байт",
"accept": [
"1500"
]
},
{
"domain": "net",
"q": "На каком уровне OSI работает обычный коммутатор?",
"a": "L2 (канальный)",
"accept": [
"l2",
"канальный",
"2"
]
},
{
"domain": "net",
"q": "На каком уровне OSI работает маршрутизатор?",
"a": "L3 (сетевой)",
"accept": [
"l3",
"сетевой",
"3"
]
},
{
"domain": "net",
"q": "Сколько байт в минимальном IPv4-заголовке?",
"a": "20",
"accept": [
"20"
]
},
{
"domain": "net",
"q": "По какому протоколу и порту работает DNS?",
"a": "UDP/53 (при больших ответах TCP/53)",
"accept": [
"udp 53",
"53",
"udp/53"
]
},
{
"domain": "net",
"q": "Что означает маска /30 в адресах?",
"a": "4 адреса: сеть, 2 узла, broadcast",
"accept": [
"4 адреса, 2 узла",
"2 узла",
"сеть+2 узла+broadcast"
]
},
{
"domain": "net",
"q": "Что делает ICMP Time Exceeded?",
"a": "сообщает, что TTL истёк (используется в traceroute)",
"accept": [
"ttl истёк",
"истёк ttl",
"сообщает об истечении ttl"
]
},
{
"domain": "c_cpp",
"q": "Чему равен sizeof(struct S { char a; int b; char c; }) на x86-64 без pack?",
"a": "12 (паддинг до выравнивания)",
"accept": [
"12",
"12 байт"
]
},
{
"domain": "c_cpp",
"q": "Что такое UB?",
"a": "undefined behavior: стандарт не накладывает требований",
"accept": [
"undefined behavior",
"неопределённое поведение",
"неопределенное поведение"
]
},
{
"domain": "c_cpp",
"q": "Что делает std::move сам по себе?",
"a": "static_cast к rvalue-ссылке; перемещение делает конструктор/присваивание",
"accept": [
"static_cast к rvalue",
"ничего, это каст",
"это каст к rvalue"
]
},
{
"domain": "c_cpp",
"q": "Чем new отличается от malloc?",
"a": "new вызывает конструктор и возвращает типизированный указатель",
"accept": [
"вызывает конструктор",
"конструктор",
"конструктор + типизированный указатель"
]
},
{
"domain": "c_cpp",
"q": "Класс владеет ресурсом и имеет деструктор, копирование не объявлено. Что будет?",
"a": "сгенерируется поверхностное копирование — двойное освобождение",
"accept": [
"двойное освобождение",
"двойное free",
"поверхностное копирование"
]
},
{
"domain": "c_cpp",
"q": "Зачем нужен виртуальный деструктор у базового класса?",
"a": "иначе delete через Base* — UB, деструктор наследника не вызовется",
"accept": [
"деструктор наследника не вызовется",
"иначе ub",
"чтобы вызвался деструктор наследника"
]
},
{
"domain": "c_cpp",
"q": "Что делает const после сигнатуры метода?",
"a": "делает this const, запрещает менять поля",
"accept": [
"this const",
"запрещает менять поля",
"метод не меняет объект"
]
},
{
"domain": "c_cpp",
"q": "Определён ли стандартом порядок вычисления аргументов f(a(), b())?",
"a": "нет, порядок не задан",
"accept": [
"нет",
"не определён",
"не задан",
"неопределён"
]
},
{
"domain": "c_cpp",
"q": "Что проверяет ASAN?",
"a": "выходы за границы, use-after-free, утечки",
"accept": [
"выходы за границы",
"use-after-free",
"память: границы и use-after-free"
]
},
{
"domain": "c_cpp",
"q": "Что проверяет UBSAN?",
"a": "неопределённое поведение: overflow, сдвиги, выравнивание",
"accept": [
"undefined behavior",
"неопределённое поведение",
"overflow"
]
},
{
"domain": "c_cpp",
"q": "Правило трёх/пяти — в чём суть?",
"a": "владеющий ресурсом класс задаёт деструктор, копирование и перемещение",
"accept": [
"деструктор + копирование + перемещение",
"деструктор, копирование, перемещение",
"нужно определить деструктор и копирование/перемещение"
]
},
{
"domain": "c_cpp",
"q": "Чем unique_ptr отличается от shared_ptr?",
"a": "unique_ptr — единственный владелец без счётчика; shared_ptr считает ссылки",
"accept": [
"единственный владелец",
"нет счётчика ссылок",
"счётчик ссылок у shared_ptr"
]
}
]
}
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""Тренажёр «числа и факты» — добор по промахам квиза (22.09: algo 1/8, linux 3/8, net 3/8).
Использование (из каталога diag):
python3 facts_drill.py # интерактивно, 20 случайных карточек
python3 facts_drill.py --count 60 --all # все 60
python3 facts_drill.py --domain algo # только алгоритмы
python3 facts_drill.py --list # вывести все вопросы с ответами (читать)
python3 facts_drill.py --answers "20|62|ttl|..." # проверить строку ответов
python3 facts_drill.py --answers "..." --count 60 --seed 42 # тот же порядок, что в прогоне
python3 facts_drill.py --selftest # проверить сам набор карточек
Ответы сравниваются нестрого: регистр, «ё», тире и лишние пробелы не важны.
Результат пишется в results_facts.json.
"""
import argparse
import json
import os
import random
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
FACTS_PATH = os.path.join(HERE, "facts.json")
def norm(s: str) -> str:
s = (s or "").lower().replace("ё", "е")
s = s.replace("–", "-").replace("—", "-").replace("−", "-")
s = re.sub(r"[^0-9a-zа-я\-\s]", " ", s)
return re.sub(r"\s+", " ", s).strip()
def load():
with open(FACTS_PATH, encoding="utf-8") as f:
return json.load(f)["facts"]
def check(fact, answer: str) -> bool:
a = norm(answer)
if not a:
return False
return any(a == norm(x) for x in fact.get("accept", []))
def selftest(facts) -> int:
problems = []
for i, f in enumerate(facts, 1):
for key in ("domain", "q", "a", "accept"):
if not f.get(key):
problems.append(f"карточка {i}: пустое поле {key}")
if f.get("accept") and not any(norm(x) for x in f["accept"]):
problems.append(f"карточка {i}: варианты ответа нормализуются в пустоту")
if not check(f, f["accept"][0]):
problems.append(f"карточка {i}: канонический ответ не проходит проверку")
doms = {}
for f in facts:
doms[f["domain"]] = doms.get(f["domain"], 0) + 1
print(f"карточек: {len(facts)}; по доменам: {doms}")
if problems:
print("ПРОБЛЕМЫ:")
for p in problems:
print(" -", p)
return 1
print("SELFTEST OK")
return 0
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--answers", default=None, help="ответы через | (тот же порядок, что в интерактиве)")
ap.add_argument("--count", type=int, default=20)
ap.add_argument("--all", action="store_true", help="взять все карточки")
ap.add_argument("--domain", default=None, choices=["algo", "linux", "net", "c_cpp"])
ap.add_argument("--seed", type=int, default=42)
ap.add_argument("--list", action="store_true")
ap.add_argument("--selftest", action="store_true")
args = ap.parse_args()
facts = load()
if args.selftest:
sys.exit(selftest(facts))
if args.domain:
facts = [f for f in facts if f["domain"] == args.domain]
if not facts:
print("нет карточек под фильтр")
sys.exit(1)
if args.list:
for i, f in enumerate(facts, 1):
print(f"{i:2d}. [{f['domain']}] {f['q']}")
print(f" -> {f['a']}")
print(f"\nвсего: {len(facts)} (полный ответ в facts.json: {FACTS_PATH})")
sys.exit(0)
rng = random.Random(args.seed)
pool = facts[:]
rng.shuffle(pool)
picked = pool if args.all else pool[:min(args.count, len(pool))]
results = []
if args.answers is not None:
given = [x.strip() for x in args.answers.split("|")]
if len(given) < len(picked):
print(f"ответов меньше, чем карточек: {len(given)} < {len(picked)} (проверяю сколько есть)")
for i, f in enumerate(picked):
ans = given[i] if i < len(given) else ""
results.append({"n": i + 1, "domain": f["domain"], "q": f["q"], "answer": ans,
"ok": check(f, ans), "correct": f["a"]})
else:
print(f"Карточек в прогоне: {len(picked)}. Пустой ввод = не знаю, 'q' = выход.\n")
for i, f in enumerate(picked, 1):
print(f"[{i}/{len(picked)}] ({f['domain']}) {f['q']}")
try:
ans = input(" > ").strip()
except (EOFError, KeyboardInterrupt):
print()
break
if ans.lower() in ("q", "quit", "выход"):
break
ok = check(f, ans)
print(" ok" if ok else " нет. верно: " + f["a"])
results.append({"n": len(results) + 1, "domain": f["domain"], "q": f["q"],
"answer": ans, "ok": ok, "correct": f["a"]})
ok = sum(1 for r in results if r["ok"])
total = len(results)
per = {}
for r in results:
d = per.setdefault(r["domain"], {"total": 0, "ok": 0})
d["total"] += 1
d["ok"] += 1 if r["ok"] else 0
with open(os.path.join(HERE, "results_facts.json"), "w", encoding="utf-8") as fh:
json.dump({"seed": args.seed, "correct": ok, "total": total,
"per_domain": per, "details": results}, fh, ensure_ascii=False, indent=2)
print(f"\n=== ИТОГ КАРТОЧЕК: {ok}/{total} ===")
for d, v in sorted(per.items()):
share = 100 * v["ok"] / v["total"] if v["total"] else 0
mark = "ок" if share >= 80 else ("добор" if share >= 50 else "дыра")
print(f" {d:8s} {v['ok']}/{v['total']} ({share:.0f}%) — {mark}")
print("Подробности: results_facts.json")
sys.exit(0 if total and ok == total else 1)
if __name__ == "__main__":
main()
+5
View File
@@ -37,6 +37,10 @@ SPEC = {
"07_epoll": ("bin", "test_epoll.py", 300),
"08_bash": ("bash", "test_bash.py", 300),
"09_gdb": ("py", "test_gdb.py", 300),
"10_hash": ("cpp", "test_hash.cpp", 300),
"11_heap": ("cpp", "test_heap.cpp", 600),
"12_dijkstra":("cpp", "test_dijkstra.cpp",600),
"13_subnet": ("cpp", "test_subnet.cpp", 120),
}
@@ -151,6 +155,7 @@ def main():
for fn in os.listdir(SUB):
os.chmod(os.path.join(SUB, fn), 0o644)
print(f"Ответы скопированы в {SUB}")
print("Пакет: 01-09 диагностика, 10-13 добор по слабым доменам (см. WEEK7.md).")
sys.exit(0 if done == total else 1)
+31
View File
@@ -0,0 +1,31 @@
// Задача 10 — хеш-таблица с открытой адресацией.
// Реализуй методы ниже. Заглушка намеренно не проходит проверку.
#include <cstddef>
#include <vector>
class HashTable {
public:
explicit HashTable(std::size_t initial_capacity = 16) {
(void)initial_capacity;
}
void put(int key, int value) {
(void)key;
(void)value;
}
bool get(int key, int& out) const {
(void)key;
(void)out;
return false;
}
bool erase(int key) {
(void)key;
return false;
}
std::size_t size() const { return 0; }
std::size_t capacity() const { return 16; }
};
+32
View File
@@ -0,0 +1,32 @@
# Задача 10 — хеш-таблица с открытой адресацией (C++)
Свой контейнер, без `std::unordered_map`. Разрешение коллизий — **линейное зондирование**
(linear probing), ёмкость — степень двойки, при загрузке > 0.7 таблица **перехешируется**
вдвое. Индексация — через маску `idx & (capacity - 1)`, а не `%`.
```c++
class HashTable {
public:
explicit HashTable(std::size_t initial_capacity = 16);
void put(int key, int value); // обновляет значение, если ключ уже есть
bool get(int key, int& out) const; // false — ключа нет
bool erase(int key); // false — ключа не было
std::size_t size() const;
std::size_t capacity() const; // степень двойки, >= 16
};
```
Требования:
- `put` 100 000 ключей — суммарно быстрее 2 секунд (в среднем O(1));
- ключи с совпадающими младшими битами (например, все кратные 16) обязаны находиться —
это проверка зондирования, а не «повезло с хешем»;
- после 100 000 вставок `capacity()` растёт (степень двойки, загрузка ≤ 0.7), а `size()`
честно считает элементы, включая обновления существующих ключей;
- удаление должно работать: после `erase` ключ не находится, а поиск другого ключа,
стоявшего за ним в цепочке зондирования, по-прежнему работает.
Проверка: `python3 grade.py 10`. Критерий: все `ok`, сборка без предупреждений,
ASAN/UBSAN чистые.
Разбор после сдачи: почему ёмкость — степень двойки; чем линейное зондирование лучше
цепочек по кэшу и хуже по кластеризации; как tombstone-метки спасают поиск после удаления.
+78
View File
@@ -0,0 +1,78 @@
#include <cstdio>
#include <cstdint>
#include <random>
#include <chrono>
#include <unordered_map>
#include <vector>
#include "solution.cpp"
static int failures = 0;
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
int main() {
{
HashTable t;
CHECK(t.size() == 0, "новая таблица пуста");
CHECK(t.capacity() >= 16 && (t.capacity() & (t.capacity() - 1)) == 0,
"capacity — степень двойки и >= 16");
int v = -1;
CHECK(t.get(42, v) == false, "get по отсутствующему ключу -> false");
CHECK(t.erase(42) == false, "erase отсутствующего -> false");
}
{
HashTable t;
for (int i = 0; i < 1000; ++i) t.put(i, i * 7);
CHECK(t.size() == 1000, "1000 ключей: size == 1000");
bool all = true;
for (int i = 0; i < 1000; ++i) { int v = -1; if (!t.get(i, v) || v != i * 7) all = false; }
CHECK(all, "1000 ключей: все значения читаются");
t.put(500, 999);
int v = -1;
CHECK(t.get(500, v) && v == 999, "повторный put обновляет значение");
CHECK(t.size() == 1000, "повторный put не увеличивает size");
}
{
// ключи с одинаковыми младшими битами — проверка зондирования
HashTable t;
for (int i = 0; i < 512; ++i) t.put(i * 16, i);
bool all = true;
for (int i = 0; i < 512; ++i) { int v = -1; if (!t.get(i * 16, v) || v != i) all = false; }
CHECK(all, "512 ключей, кратных 16: все находятся (линейное зондирование)");
int missing = -1;
CHECK(t.get(15, missing) == false, "нечётный отсутствующий ключ -> false");
}
{
// удаление посреди цепочки зондирования
HashTable t;
for (int i = 0; i < 200; ++i) t.put(i * 16, i);
CHECK(t.erase(0) == true, "erase существующего -> true");
int v = -1;
CHECK(t.get(0, v) == false, "удалённый ключ не найден");
bool rest = true;
for (int i = 1; i < 200; ++i) { int x = -1; if (!t.get(i * 16, x) || x != i) rest = false; }
CHECK(rest, "остальные ключи из цепочки живы после удаления");
CHECK(t.size() == 199, "size после удаления == 199");
}
{
const int N = 100000;
HashTable t;
std::mt19937 rng(4242);
std::vector<int> keys(N);
for (auto& k : keys) k = int(rng() % 1000000);
auto t0 = std::chrono::steady_clock::now();
for (int i = 0; i < N; ++i) t.put(keys[i], i);
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
std::printf(" (100k вставок: time=%.3fs, capacity=%zu, size=%zu)\n", sec, t.capacity(), t.size());
CHECK(sec < 2.0, "100k вставок за < 2 c");
CHECK(t.size() >= 95000 && t.size() <= 100000, "size в разумных пределах");
CHECK(t.capacity() >= 131072 && t.capacity() <= 262144, "capacity выросла (загрузка <= 0.7)");
bool all = true;
for (int i = 0; i < N; ++i) { int v = -1; if (!t.get(keys[i], v)) all = false; }
CHECK(all, "все 100k ключей читаются");
int v = -1;
CHECK(t.get(-12345, v) == false, "отсутствующий ключ -> false");
}
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
return failures ? 1 : 0;
}
+19
View File
@@ -0,0 +1,19 @@
// Задача 11 — куча: heapify за O(n), kth_largest, top_k.
// Реализуй функции ниже. Заглушка намеренно не проходит проверку.
#include <vector>
void heapify(std::vector<int>& a) {
(void)a;
}
int kth_largest(const std::vector<int>& a, int k) {
(void)a;
(void)k;
return 0;
}
std::vector<int> top_k(const std::vector<int>& a, int k) {
(void)a;
(void)k;
return {};
}
+24
View File
@@ -0,0 +1,24 @@
# Задача 11 — куча: построение за O(n) и top-K (C++)
Куча **max-heap** в массиве (для элемента `i` дети — `2i+1`, `2i+2`), без `std::priority_queue`.
```c++
void heapify(std::vector<int>& a); // перестроить массив в кучу
int kth_largest(const std::vector<int>& a, int k); // k >= 1
std::vector<int> top_k(const std::vector<int>& a, int k); // k наибольших, по убыванию
```
Требования:
- `heapify` — **просеивание снизу вверх (bottom-up), O(n)**, а не `push` в цикле (это O(n log n));
проверка свойств кучи машинная, требование по сложности я сверяю глазами по коду — на
собеседовании спросят именно это;
- `heapify` обязан сохранить мультимножество элементов (ничего не терять и не дублировать);
- `k` в границах `1..n`; при `k > n` — `top_k` возвращает все по убыванию, `kth_largest` при `k > n`
возвращает значение минимального элемента (то есть не падает);
- 3 000 000 элементов: `heapify` быстрее 2 секунд, `kth_largest` с `k = 1000` быстрее 2 секунд.
Проверка: `python3 grade.py 11`. Критерий: все `ok`, сборка без предупреждений,
ASAN/UBSAN чистые.
Разбор после сдачи: почему снизу вверх выходит сумма геометрической прогрессии; когда
нужен min-heap размера k (top-K на потоке); чем `nth_element` отличается от кучи.
+74
View File
@@ -0,0 +1,74 @@
#include <cstdio>
#include <vector>
#include <random>
#include <chrono>
#include <algorithm>
#include "solution.cpp"
static int failures = 0;
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
static bool is_max_heap(const std::vector<int>& a) {
for (std::size_t i = 1; i < a.size(); ++i)
if (a[(i - 1) / 2] < a[i]) return false;
return true;
}
int main() {
{
std::vector<int> a = {3, 1, 4, 1, 5, 9, 2, 6};
std::vector<int> before = a;
heapify(a);
CHECK(is_max_heap(a), "маленький массив: свойство max-heap держится");
std::vector<int> s1 = before, s2 = a;
std::sort(s1.begin(), s1.end()); std::sort(s2.begin(), s2.end());
CHECK(s1 == s2, "мультимножество сохранено");
std::vector<int> empty_a;
heapify(empty_a);
CHECK(empty_a.empty(), "пустой массив -> пустой");
std::vector<int> one = {7};
heapify(one);
CHECK(one.size() == 1 && one[0] == 7, "один элемент не теряется");
}
{
std::vector<int> a = {1, 2, 3, 4, 5};
CHECK(kth_largest(a, 1) == 5, "kth_largest k=1 -> максимум");
CHECK(kth_largest(a, 5) == 1, "kth_largest k=n -> минимум");
CHECK(kth_largest(a, 3) == 3, "kth_largest k=3 -> медиана");
std::vector<int> z = {4, 4, 4};
CHECK(kth_largest(z, 2) == 4, "дубликаты: k=2 -> 4");
std::vector<int> dup = {5, 5, 1, 1, 3};
CHECK(kth_largest(dup, 4) == 1, "дубликаты: k=4 -> 1");
std::vector<int> big = top_k(a, 3);
CHECK(big.size() == 3 && big[0] == 5 && big[1] == 4 && big[2] == 3, "top_k(3) по убыванию");
std::vector<int> all = top_k(a, 10);
CHECK(all.size() == 5 && all.front() == 5 && all.back() == 1, "top_k(k>n) -> все по убыванию");
std::vector<int> neg = {-5, -1, -9};
CHECK(kth_largest(neg, 1) == -1, "отрицательные: максимум -1");
CHECK(kth_largest(a, 100) == 1, "k>n не падает и даёт минимум");
}
{
const int N = 3000000;
std::mt19937 rng(777);
std::vector<int> a(N);
for (auto& x : a) x = int(rng() % 1000000);
std::vector<int> ref = a;
auto t0 = std::chrono::steady_clock::now();
heapify(a);
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
std::printf(" (3M heapify: time=%.3fs, top=%d)\n", sec, a.empty() ? -1 : a[0]);
CHECK(is_max_heap(a), "3M: свойство max-heap держится");
CHECK(a[0] == *std::max_element(ref.begin(), ref.end()), "3M: корень == максимум");
CHECK(sec < 2.0, "3M heapify за < 2 c");
t0 = std::chrono::steady_clock::now();
int k = kth_largest(ref, 1000);
double sec2 = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
std::sort(ref.begin(), ref.end(), std::greater<int>());
std::printf(" (3M kth_largest k=1000: time=%.3fs, value=%d)\n", sec2, k);
CHECK(k == ref[999], "3M: kth_largest(k=1000) совпадает с сортировкой");
CHECK(sec2 < 3.0, "3M kth_largest за < 3 c");
}
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
return failures ? 1 : 0;
}
+14
View File
@@ -0,0 +1,14 @@
// Задача 12 — кратчайший путь (Дейкстра).
// Реализуй функцию ниже. Заглушка намеренно не проходит проверку.
#include <vector>
#include <tuple>
long long shortest_path(int n,
const std::vector<std::tuple<int, int, int>>& edges,
int src, int dst) {
(void)n;
(void)edges;
(void)src;
(void)dst;
return -1;
}
+21
View File
@@ -0,0 +1,21 @@
# Задача 12 — Дейкстра на priority_queue (C++)
```c++
long long shortest_path(int n,
const std::vector<std::tuple<int,int,int>>& edges, // (u, v, w)
int src, int dst);
```
Требования:
- рёбра **ориентированные**, вес `w >= 0`, self-loop допустим; вершины `0..n-1`;
- вернуть длину кратчайшего пути `src -> dst` (`long long`, веса суммируются до 10^9);
- недостижимость → `-1`; `src == dst` → `0`;
- вес рёбер 0 допустим (условие применимости Дейкстры — **неотрицательные** веса);
- 100 000 вершин и 200 000 рёбер: быстрее 2 секунд. Значит нужна `std::priority_queue`
(O((V+E) log V)), а не O(V²) перебор минимума.
Подсказки по разбору (после сдачи): «ленивое» удаление устаревших записей из очереди
(`if (d != dist[v]) continue;`), почему нельзя Дейкстру с отрицательными рёбрами и когда
нужен Беллман–Форд.
Проверка: `python3 grade.py 12`. Критерий: все `ok`, сборка без предупреждений.
+58
View File
@@ -0,0 +1,58 @@
#include <cstdio>
#include <vector>
#include <tuple>
#include <random>
#include <chrono>
#include "solution.cpp"
static int failures = 0;
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
using E = std::tuple<int, int, int>;
int main() {
{
std::vector<E> e = {{0, 1, 4}, {0, 2, 1}, {2, 1, 1}, {1, 3, 1}};
CHECK(shortest_path(4, e, 0, 3) == 3, "0->3 через 2: 1+1+1 = 3");
CHECK(shortest_path(4, e, 0, 0) == 0, "src == dst -> 0");
CHECK(shortest_path(4, e, 3, 0) == -1, "обратного пути нет -> -1");
CHECK(shortest_path(4, e, 0, 2) == 1, "прямой путь 0->2 = 1");
}
{
std::vector<E> e = {{0, 1, 5}, {0, 2, 1}, {2, 1, 1}};
CHECK(shortest_path(3, e, 0, 1) == 2, "длинное ребро не должно побеждать короткий путь");
std::vector<E> z = {{0, 1, 0}, {1, 2, 0}};
CHECK(shortest_path(3, z, 0, 2) == 0, "нулевые веса допустимы");
std::vector<E> self = {{0, 0, 7}, {0, 1, 2}};
CHECK(shortest_path(2, self, 0, 1) == 2, "self-loop не ломает поиск");
std::vector<E> par = {{0, 1, 3}, {0, 1, 7}};
CHECK(shortest_path(2, par, 0, 1) == 3, "параллельные рёбра: берём минимальное");
std::vector<E> none;
CHECK(shortest_path(2, none, 0, 1) == -1, "без рёбер -> -1");
}
{
// цепочка 0-1-2-...-(n-1) по 1000, плюс «ловушки»: обратные рёбра по 100000.
// Ответ детерминирован: идти по цепочке дешевле, чем прыгать назад и снова вперёд.
const int n = 100000;
std::vector<E> e;
e.reserve(200000);
for (int i = 0; i + 1 < n; ++i) e.emplace_back(i, i + 1, 1000);
for (int i = 1; i < n; ++i) e.emplace_back(i, i - 1, 100000);
e.emplace_back(0, 0, 99999999);
auto t0 = std::chrono::steady_clock::now();
long long d = shortest_path(n, e, 0, n - 1);
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
std::printf(" (100k вершин, 200k рёбер: dist=%lld, time=%.3fs)\n", d, sec);
CHECK(d == (long long)(n - 1) * 1000, "цепочка: кратчайший путь по цепочке");
CHECK(sec < 2.0, "100k вершин за < 2 c");
}
{
// большие веса: сумма должна влезать в long long
std::vector<E> e;
for (int i = 0; i + 1 < 2000; ++i) e.emplace_back(i, i + 1, 1000000);
CHECK(shortest_path(2000, e, 0, 1999) == 1999LL * 1000000LL, "большие веса: 1999 * 1e6");
}
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
return failures ? 1 : 0;
}
+29
View File
@@ -0,0 +1,29 @@
// Задача 13 — арифметика подсетей IPv4.
// Реализуй функции ниже. Заглушка намеренно не проходит проверку.
#include <cstdint>
struct Subnet {
uint32_t network = 0;
uint32_t broadcast = 0;
uint32_t first_host = 0;
uint32_t last_host = 0;
long long host_count = 0;
};
Subnet subnet_of(uint32_t ip, int prefix) {
(void)ip;
(void)prefix;
return Subnet{};
}
bool same_subnet(uint32_t a, uint32_t b, int prefix) {
(void)a;
(void)b;
(void)prefix;
return false;
}
int prefix_for_hosts(int hosts) {
(void)hosts;
return -1;
}
+36
View File
@@ -0,0 +1,36 @@
# Задача 13 — арифметика подсетей IPv4 (C++)
Адреса передаются в **host byte order** (`0x0A000001` = 10.0.0.1), маска — длиной префикса.
```c++
struct Subnet {
uint32_t network; // адрес сети
uint32_t broadcast; // широковещательный адрес
uint32_t first_host; // первый адрес для узла
uint32_t last_host; // последний адрес для узла
long long host_count; // сколько адресов узлов доступно (для /0 не влезает в int)
};
Subnet subnet_of(uint32_t ip, int prefix); // 0 <= prefix <= 32
bool same_subnet(uint32_t a, uint32_t b, int prefix);
int prefix_for_hosts(int hosts); // самая узкая подсеть, куда влезает hosts
```
Определения, которые нужно соблюсти:
- для `prefix <= 30`: `host_count = 2^(32-prefix) - 2`, `first_host = network + 1`,
`last_host = broadcast - 1`;
- `/31` (RFC 3021): `host_count = 2`, `first_host = network`, `last_host = broadcast`;
- `/32`: `host_count = 1`, `first_host = last_host = network = broadcast = ip`;
- `prefix_for_hosts(h)`: **наибольший** `prefix` из диапазона `0..30` (то есть самая узкая
подсеть), при котором `host_count >= h`. `h < 1` или `h > 2^32-2` → `-1`.
Исключения `/31` и `/32` в подборе не участвуют — они для линков и одиночных адресов.
Эталонные примеры, которые обязаны сойтись: `/26` → 62 узла; `/24` → 254; `/30` → 2;
`10.0.1.130/26` → сеть `10.0.1.128`, broadcast `10.0.1.191`, узлы `129..190`;
`prefix_for_hosts(62)` → 26, `prefix_for_hosts(63)` → 25, `prefix_for_hosts(254)` → 24,
`prefix_for_hosts(1)` → 30.
Проверка: `python3 grade.py 13`. Критерий: все `ok`, сборка без предупреждений.
Разбор после сдачи: как считать префикс по числу узлов за O(1) (`32 - ceil(log2(h+2))`),
почему `/31` — исключение, что такое маска в бинарном виде.
+66
View File
@@ -0,0 +1,66 @@
#include <cstdio>
#include <cstdint>
#include "solution.cpp"
static int failures = 0;
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
static uint32_t ip4(int a, int b, int c, int d) {
return (uint32_t(a) << 24) | (uint32_t(b) << 16) | (uint32_t(c) << 8) | uint32_t(d);
}
int main() {
{
Subnet s = subnet_of(ip4(10, 0, 1, 130), 26);
CHECK(s.network == ip4(10, 0, 1, 128), "/26: адрес сети 10.0.1.128");
CHECK(s.broadcast == ip4(10, 0, 1, 191), "/26: broadcast 10.0.1.191");
CHECK(s.first_host == ip4(10, 0, 1, 129), "/26: первый узел 10.0.1.129");
CHECK(s.last_host == ip4(10, 0, 1, 190), "/26: последний узел 10.0.1.190");
CHECK(s.host_count == 62, "/26: 62 узла (не 64)");
}
{
Subnet s = subnet_of(ip4(192, 168, 5, 200), 24);
CHECK(s.network == ip4(192, 168, 5, 0), "/24: сеть 192.168.5.0");
CHECK(s.broadcast == ip4(192, 168, 5, 255), "/24: broadcast 192.168.5.255");
CHECK(s.host_count == 254, "/24: 254 узла");
Subnet t = subnet_of(ip4(172, 16, 4, 9), 30);
CHECK(t.network == ip4(172, 16, 4, 8) && t.broadcast == ip4(172, 16, 4, 11),
"/30: сеть 172.16.4.8, broadcast 172.16.4.11");
CHECK(t.host_count == 2 && t.first_host == ip4(172, 16, 4, 9) && t.last_host == ip4(172, 16, 4, 10),
"/30: 2 узла, 9 и 10");
Subnet z = subnet_of(ip4(10, 0, 0, 5), 32);
CHECK(z.network == ip4(10, 0, 0, 5) && z.broadcast == ip4(10, 0, 0, 5) && z.host_count == 1,
"/32: один адрес, сеть == broadcast == узел");
Subnet p31 = subnet_of(ip4(10, 0, 0, 1), 31);
CHECK(p31.host_count == 2 && p31.first_host == ip4(10, 0, 0, 0) && p31.last_host == ip4(10, 0, 0, 1),
"/31: RFC 3021 — 2 узла без вычета");
Subnet eight = subnet_of(ip4(10, 200, 30, 40), 8);
CHECK(eight.network == ip4(10, 0, 0, 0) && eight.broadcast == ip4(10, 255, 255, 255),
"/8: сеть 10.0.0.0, broadcast 10.255.255.255");
Subnet zero = subnet_of(ip4(8, 8, 8, 8), 0);
CHECK(zero.network == 0 && zero.broadcast == 0xFFFFFFFFu && zero.host_count == 4294967294LL,
"/0: вся сеть, узлов 2^32-2");
}
{
CHECK(same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 150), 26), "/26: 130 и 150 в одной подсети");
CHECK(!same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 200), 26), "/26: 130 и 200 — разные подсети");
CHECK(!same_subnet(ip4(10, 0, 1, 130), ip4(10, 0, 1, 127), 26), "/26: 130 и 127 — разные подсети");
CHECK(same_subnet(ip4(10, 0, 1, 1), ip4(10, 0, 2, 1), 16), "/16: разные третьи октеты — одна сеть");
CHECK(!same_subnet(ip4(10, 0, 1, 1), ip4(10, 1, 1, 1), 16), "/16: 10.0.x и 10.1.x — разные");
CHECK(same_subnet(ip4(10, 0, 0, 0), ip4(10, 0, 0, 1), 31), "/31: соседи по RFC 3021");
}
{
CHECK(prefix_for_hosts(62) == 26, "prefix_for_hosts(62) -> 26");
CHECK(prefix_for_hosts(63) == 25, "prefix_for_hosts(63) -> 25 (63 в /26 не влезает)");
CHECK(prefix_for_hosts(254) == 24, "prefix_for_hosts(254) -> 24");
CHECK(prefix_for_hosts(255) == 23, "prefix_for_hosts(255) -> 23");
CHECK(prefix_for_hosts(2) == 30, "prefix_for_hosts(2) -> 30");
CHECK(prefix_for_hosts(1) == 30, "prefix_for_hosts(1) -> 30 (узкая подсеть общего вида)");
CHECK(prefix_for_hosts(0) == -1, "prefix_for_hosts(0) -> -1");
CHECK(prefix_for_hosts(4294967295) == -1, "prefix_for_hosts(2^32-1) -> -1 (не влезает)");
CHECK(prefix_for_hosts(1000000) == 12, "prefix_for_hosts(1000000) -> 12");
}
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
return failures ? 1 : 0;
}