summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGloria Mundi <gloria@gloria-mundi.eu>2024-02-27 23:01:41 +0100
committerGloria Mundi <gloria@gloria-mundi.eu>2024-02-27 23:01:41 +0100
commit496e4f909f4c6f4100ad3cef0a663b3f69b3b89f (patch)
tree2bb5ad440c683abb215fb46ffd3818da6dc0df46
parent69cccd197a4aa331647a02046e708396f6a13937 (diff)
add binary lifting test
-rw-r--r--.gitignore4
-rw-r--r--Makefile22
-rw-r--r--graph/test/binary_lifting.cpp93
3 files changed, 118 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 21eab22..2c4d2aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -220,3 +220,7 @@ TSWLatexianTemp*
*-tags.tex
*~
+
+# files produced by the testing system
+*.test
+*.ok
diff --git a/Makefile b/Makefile
index 0338a34..b92afb9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,25 @@
-all:
+TESTS = graph/test/binary_lifting.test
+
+pdf:
latexmk -pdf tcr
+
+all: pdf test
+
+test: $(TESTS:.test=.ok)
+
clean:
latexmk -c tcr
rm -f *.thm
+ rm -f $(TESTS) $(TESTS:.test=.ok)
+
+%.ok: %.test
+ timeout -v 1 ./$<
+ @touch $@
+%.test: %.cpp
+ g++ -std=gnu++20 -Wall -Wextra -Wpedantic -Werror \
+ -fsanitize=address,undefined -g -o $@ $<
+
+graph/test/binary_lifting.test: graph/test/binary_lifting.cpp \
+ graph/binary_lifting.cpp
+
+.PHONY: all pdf test clean
diff --git a/graph/test/binary_lifting.cpp b/graph/test/binary_lifting.cpp
new file mode 100644
index 0000000..e1cf7d2
--- /dev/null
+++ b/graph/test/binary_lifting.cpp
@@ -0,0 +1,93 @@
+#include <bits/stdc++.h>
+using namespace std;
+
+#include "../binary_lifting.cpp"
+
+mt19937 rd(0);
+
+void test(const vector<vector<int>> &old_adj, int root) {
+ int n = old_adj.size();
+ vector<int> perm(n);
+ iota(perm.begin(), perm.end(), 0);
+ ranges::shuffle(perm, rd);
+ vector<vector<int>> adj(n);
+ for (int i = 0; i < n; i++) {
+ auto &a = adj[perm[i]] = old_adj[i];
+ for (int &v: a) v = perm[v];
+ ranges::shuffle(a, rd);
+ }
+ root = perm[root];
+
+ vector<int> parent(n);
+ function<void(int,int)> dfs = [&](int u, int p) {
+ parent[u] = p;
+ for (int v: adj[u]) if (v != p) dfs(v, u);
+ };
+ dfs(root, -1);
+
+ function<bool(int, int)> is_ancestor = [&](int u, int v) {
+ while (v != -1 && u != v) v = parent[v];
+ return u == v;
+ };
+ function<int(int)> depth = [&](int v) {
+ int r = 0;
+ while ((v = parent[v]) != -1) r++;
+ return r;
+ };
+
+ Lift lift(adj, root);
+ for (int i = 0; i < n; i++) assert(lift.depth(i) == depth(i));
+ uniform_int_distribution<int> distrib(0, n-1);
+ for (int i = 0; i < 1000; i++) {
+ int v = distrib(rd);
+ int d = distrib(rd);
+ int u = lift.lift(v, d);
+ assert(is_ancestor(u, v));
+ if (d <= depth(v)) assert(depth(u) == d);
+ else assert(u == v);
+ }
+ for (int i = 0; i < 1000; i++) {
+ int u = distrib(rd);
+ int v = distrib(rd);
+ int lca = lift.lca(u, v);
+ assert(is_ancestor(lca, u));
+ assert(is_ancestor(lca, v));
+ for (int p = parent[lca]; int c: adj[lca]) {
+ if (c == p) continue;
+ assert(!is_ancestor(c, u) || !is_ancestor(c, v));
+ }
+ }
+}
+
+int main() {
+ {
+ // Single vertex
+ vector<vector<int>> adj(1);
+ test(adj, 0);
+ }
+ {
+ // Path
+ vector<vector<int>> adj(100);
+ for (int i = 1; i < 100; i++) {
+ adj[i-1].push_back(i);
+ adj[i].push_back(i-1);
+ }
+ test(adj, 0);
+ test(adj, 99);
+ test(adj, 40);
+ }
+ {
+ // Random
+ vector<vector<int>> adj(1000);
+ for (int i = 1; i < 1000; i++) {
+ uniform_int_distribution<int> distrib(0, i-1);
+ int p = distrib(rd);
+ adj[p].push_back(i);
+ adj[i].push_back(p);
+ }
+ test(adj, 0);
+ test(adj, 300);
+ test(adj, 600);
+ test(adj, 900);
+ }
+}