План v2 по диагностике (квиз 14/32) + 4 новые задачи (hash, heap, dijkstra, subnet) + тренажёр 60 карточек
This commit is contained in:
@@ -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; }
|
||||
};
|
||||
@@ -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-метки спасают поиск после удаления.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {};
|
||||
}
|
||||
@@ -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` отличается от кучи.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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`, сборка без предупреждений.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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` — исключение, что такое маска в бинарном виде.
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user