summaryrefslogtreecommitdiff
path: root/graph/test
diff options
context:
space:
mode:
Diffstat (limited to 'graph/test')
-rw-r--r--graph/test/LCA_sparse.cpp63
-rw-r--r--graph/test/binary_lifting.cpp67
-rw-r--r--graph/test/util.cpp60
3 files changed, 190 insertions, 0 deletions
diff --git a/graph/test/LCA_sparse.cpp b/graph/test/LCA_sparse.cpp
new file mode 100644
index 0000000..c8b0017
--- /dev/null
+++ b/graph/test/LCA_sparse.cpp
@@ -0,0 +1,63 @@
+#include "util.cpp"
+#define all(X) begin(X), end(X)
+#define sz ssize
+#include "../../datastructures/sparseTable.cpp"
+#include "../LCA_sparse.cpp"
+
+void test(vector<vector<int>> &adj, int root) {
+ int n = adj.size();
+
+ 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;
+ };
+
+ LCA lca;
+ lca.init(adj, root);
+ for (int i = 0; i < n; i++) assert(lca.getDepth(i) == depth(i));
+ for (int i = 0; i < 1000; i++) {
+ int u = util::randint(n);
+ int v = util::randint(n);
+ int l = lca.getLCA(u, v);
+ assert(is_ancestor(l, u));
+ assert(is_ancestor(l, v));
+ for (int p = parent[l]; int c: adj[l]) {
+ 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 = util::path(100);
+ int left = 0, mid = 40, right = 99;
+ util::shuffle_graph(adj, left, mid, right);
+ test(adj, left);
+ test(adj, mid);
+ test(adj, right);
+ }
+ {
+ // Random
+ vector<vector<int>> adj = util::random_tree(1000);
+ test(adj, 0);
+ }
+}
diff --git a/graph/test/binary_lifting.cpp b/graph/test/binary_lifting.cpp
new file mode 100644
index 0000000..05b903a
--- /dev/null
+++ b/graph/test/binary_lifting.cpp
@@ -0,0 +1,67 @@
+#include "util.cpp"
+#include "../binary_lifting.cpp"
+
+void test(vector<vector<int>> &adj, int root) {
+ int n = adj.size();
+
+ 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));
+ for (int i = 0; i < 1000; i++) {
+ int v = util::randint(n);
+ int d = util::randint(n);
+ 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 = util::randint(n);
+ int v = util::randint(n);
+ 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 = util::path(100);
+ int left = 0, mid = 40, right = 99;
+ util::shuffle_graph(adj, left, mid, right);
+ test(adj, left);
+ test(adj, mid);
+ test(adj, right);
+ }
+ {
+ // Random
+ vector<vector<int>> adj = util::random_tree(1000);
+ test(adj, 0);
+ }
+}
diff --git a/graph/test/util.cpp b/graph/test/util.cpp
new file mode 100644
index 0000000..8c14b5c
--- /dev/null
+++ b/graph/test/util.cpp
@@ -0,0 +1,60 @@
+
+namespace util {
+
+void shuffle_adj_lists(vector<vector<int>> &adj) {
+ for (auto &a: adj) ranges::shuffle(a, rd);
+}
+
+vector<vector<int>> random_tree(int n) {
+ vector<vector<int>> adj(n);
+
+ vector<vector<int>> components(n);
+ for (int i = 0; i < n; i++) components[i].push_back(i);
+ while (components.size() > 1) {
+ int c1 = randint(components.size());
+ int c2 = randint(components.size()-1);
+ c2 += (c2 >= c1);
+
+ int v1 = components[c1][randint(components[c1].size())];
+ int v2 = components[c2][randint(components[c2].size())];
+
+ adj[v1].push_back(v2);
+ adj[v2].push_back(v1);
+
+ if (components[c1].size() < components[c2].size()) swap(c1, c2);
+ for (int v: components[c2]) components[c1].push_back(v);
+ swap(components[c2], components.back());
+ components.pop_back();
+ }
+
+ shuffle_adj_lists(adj);
+ return adj;
+}
+
+vector<vector<int>> path(int n) {
+ vector<vector<int>> adj(n);
+ for (int i = 1; i < n; i++) {
+ adj[i-1].push_back(i);
+ adj[i].push_back(i-1);
+ }
+ return adj;
+}
+
+template<typename... Ts>
+void shuffle_graph(vector<vector<int>> &adj, Ts &...vertex) {
+ int n = adj.size();
+ vector<int> perm(n);
+ iota(perm.begin(), perm.end(), 0);
+ ranges::shuffle(perm, rd);
+
+ vector<vector<int>> new_adj(n);
+ for (int i = 0; i < n; i++) {
+ auto &a = new_adj[perm[i]] = move(adj[i]);
+ for (int &v: a) v = perm[v];
+ }
+ adj = move(new_adj);
+ shuffle_adj_lists(adj);
+ ((vertex = perm[vertex]), ...);
+}
+
+}