diff options
Diffstat (limited to 'test')
130 files changed, 9185 insertions, 0 deletions
diff --git a/test/datastructures/bitset.cpp b/test/datastructures/bitset.cpp new file mode 100644 index 0000000..2ba61a5 --- /dev/null +++ b/test/datastructures/bitset.cpp @@ -0,0 +1,6 @@ +#include "../util.h" + +int main() { + int x = 0; + #include <datastructures/bitset.cpp> +} diff --git a/test/datastructures/fenwickTree.cpp b/test/datastructures/fenwickTree.cpp new file mode 100644 index 0000000..c1ef6bf --- /dev/null +++ b/test/datastructures/fenwickTree.cpp @@ -0,0 +1,58 @@ +#include "../util.h" +#include <datastructures/fenwickTree.cpp> + +//void update(int i, ll val) +//void init(int n) +//prefix_sum(int i) + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + init(n); + vector<ll> naive(n); + for (int operations = 0; operations < 1000; operations++) { + { + int i = Random::integer<int>(0, n); + ll x = Random::integer<ll>(-1000, 1000); + update(i, x); + naive[i] += x; + } + { + queries++; + int i = Random::integer<int>(0, n); + ll got = prefix_sum(i); + ll expected = 0; + for (int j = 0; j <= i; j++) expected += naive[j]; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + t.start(); + init(N); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + int i = Random::integer<int>(0, N); + int j = Random::integer<int>(0, N); + ll x = Random::integer<ll>(-1000, 1000); + + t.start(); + update(i, x); + hash ^= prefix_sum(j); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/fenwickTree2.cpp b/test/datastructures/fenwickTree2.cpp new file mode 100644 index 0000000..aa99576 --- /dev/null +++ b/test/datastructures/fenwickTree2.cpp @@ -0,0 +1,60 @@ +#include "../util.h" +#include <datastructures/fenwickTree2.cpp> + +//void update(int l, int r, ll val) +//void init(int n) +//prefix_sum(int i) + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + vector<ll> naive(n);// = Random::integers<ll>(n, -1000, 1000); + init(naive); + for (int operations = 0; operations < 1000; operations++) { + { + auto [i, j] = Random::pair<int>(0, n); + ll x = Random::integer<ll>(-1000, 1000); + update(i, j, x); + for (int k = i; k < j; k++) naive[k] += x; + } + { + queries++; + int i = Random::integer<int>(0, n); + ll got = prefix_sum(i); + ll expected = 0; + for (int j = 0; j <= i; j++) expected += naive[j]; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + vector<ll> tmp = Random::integers<ll>(N, -1000, 1000); + t.start(); + init(tmp); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + int i = Random::integer<int>(0, N); + int j = Random::integer<int>(0, N); + int k = Random::integer<int>(0, N); + ll x = Random::integer<ll>(-1000, 1000); + + t.start(); + update(i, j, x); + hash ^= prefix_sum(k); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/lazyPropagation.cpp b/test/datastructures/lazyPropagation.cpp new file mode 100644 index 0000000..7002061 --- /dev/null +++ b/test/datastructures/lazyPropagation.cpp @@ -0,0 +1,61 @@ +#include "../util.h" +constexpr ll inf = LL::INF; +#include <datastructures/lazyPropagation.cpp> + +constexpr int N = 1000'000; + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + vector<ll> naive = Random::integers<ll>(n, -1000, 1000); + SegTree tree(naive); + for (int operations = 0; operations < 1000; operations++) { + { + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + ll x = Random::integer<ll>(-1000, 1000); + tree.update(l, r, x); + for (int j = l; j < r; j++) naive[j] = x; + } + { + queries++; + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + ll got = tree.query(l, r); + ll expected = 0; + for (int j = l; j < r; j++) expected += naive[j]; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test() { + timer t; + t.start(); + vector<ll> tmp(N); + SegTree tree(tmp); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + auto [l1, r1] = Random::pair<int>(0, N + 1); + auto [l2, r2] = Random::pair<int>(0, N + 1); + ll x1 = Random::integer<ll>(-1000, 1000); + + t.start(); + tree.update(l1, r1, x1); + hash ^= tree.query(l2, r2); + t.stop(); + } + if (t.time > 2000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/pbds.cpp b/test/datastructures/pbds.cpp new file mode 100644 index 0000000..9080332 --- /dev/null +++ b/test/datastructures/pbds.cpp @@ -0,0 +1,11 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> + +int main() { + Tree<int> t1, t2; + swap(t1, t2); + hashSet<int> s1, s2; + swap(s1, s2); + hashMap<int, int> m1, m2; + swap(m1, m2); +}
\ No newline at end of file diff --git a/test/datastructures/segmentTree.cpp b/test/datastructures/segmentTree.cpp new file mode 100644 index 0000000..fbac13e --- /dev/null +++ b/test/datastructures/segmentTree.cpp @@ -0,0 +1,122 @@ +#include "../util.h" +#include <datastructures/segmentTree.cpp> + +constexpr int N = 1'000'000; + +//void update(int i, ll val) +//ll query(int l, int r) + +//point update + range query +void stress_test1() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + vector<ll> naive = Random::integers<ll>(n, -1000, 1000); + SegTree tree(naive); + for (int operations = 0; operations < 1000; operations++) { + { + int i = Random::integer<int>(0, n); + ll x = Random::integer<ll>(-1000, 1000); + tree.update(i, x); + naive[i] = x;//point assignment + } + { + queries++; + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + ll got = tree.query(l, r); + ll expected = 0; + for (int j = l; j < r; j++) expected += naive[j];//range sum + if (got != expected) cerr << " got: " << got << ", expected: " << expected << FAIL; + } + } + } + cerr << " tested random queries: " << queries << endl; +} + +//point update + range query +void performance_test1() { + timer t; + t.start(); + vector<ll> tmp(N); + SegTree tree(tmp); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + int i = Random::integer<int>(0, N); + auto [l, r] = Random::pair<int>(0, N + 1); + ll x = Random::integer<ll>(-1000, 1000); + + t.start(); + tree.update(i, x); + hash ^= tree.query(l, r); + t.stop(); + } + if (t.time > 1000) cerr << " too slow: " << t.time << FAIL; + cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +//void modify(int l, int r, T val) +//ll query(int i) + +//range update + point query +void stress_test2() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + vector<ll> naive(n); + SegTree tree(naive); + naive = Random::integers<ll>(n, -1000, 1000); + copy(all(naive), tree.tree.begin() + n); + for (int operations = 0; operations < 1000; operations++) { + { + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + ll x = Random::integer<ll>(-1000, 1000); + tree.modify(l, r, x); + for (int j = l; j < r; j++) naive[j] += x;//range add + } + { + queries++; + int i = Random::integer<int>(0, n); + ll got = tree.query(i); + ll expected = naive[i];//point query + if (got != expected) cerr << " got: " << got << ", expected: " << expected << FAIL; + } + } + } + cerr << " tested random queries: " << queries << endl; +} + +//range update + point query +void performance_test2() { + timer t; + t.start(); + vector<ll> tmp(N); + SegTree tree(tmp); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + int i = Random::integer<int>(0, N); + auto [l, r] = Random::pair<int>(0, N + 1); + ll x = Random::integer<ll>(-1000, 1000); + + t.start(); + tree.modify(l, r, x); + hash ^= tree.query(i); + t.stop(); + } + if (t.time > 1000) cerr << " too slow: " << t.time << FAIL; + cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + cerr << "point update + range query:" << endl; + stress_test1(); + performance_test1(); + cerr << "range update + point query" << endl; + stress_test2(); + performance_test2(); +} diff --git a/test/datastructures/sparseTable.cpp b/test/datastructures/sparseTable.cpp new file mode 100644 index 0000000..7577694 --- /dev/null +++ b/test/datastructures/sparseTable.cpp @@ -0,0 +1,51 @@ +#include "../util.h" +constexpr ll INF = LL::INF; +#include <datastructures/sparseTable.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 1000; tries++) { + int n = Random::integer<int>(1, 100); + vector<ll> naive = Random::integers<ll>(n, -1000, 1000); + SparseTable st; + st.init(&naive); + for (int operations = 0; operations < 1000; operations++) { + queries++; + int l = Random::integer<int>(0, n+1); + int r = Random::integer<int>(0, n+1); + + ll got = st.queryIdempotent(l, r); + ll expected = r <= l ? -1 : l; + for (int j = l; j < r; j++) { + if (naive[j] < naive[expected]) expected = j; + } + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'500'000; +void performance_test() { + timer t; + vector<ll> naive = Random::integers<ll>(N, -1000, 1000); + t.start(); + SparseTable st; + st.init(&naive); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + auto [l, r] = Random::pair<int>(0, N+1); + + t.start(); + hash += st.queryIdempotent(l, r); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/sparseTableDisjoint.cpp b/test/datastructures/sparseTableDisjoint.cpp new file mode 100644 index 0000000..77bb005 --- /dev/null +++ b/test/datastructures/sparseTableDisjoint.cpp @@ -0,0 +1,48 @@ +#include "../util.h" +#include <datastructures/sparseTableDisjoint.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 1000; tries++) { + int n = Random::integer<int>(1, 100); + vector<ll> naive = Random::integers<ll>(n, -1000, 1000); + DisjointST st; + st.init(&naive); + for (int operations = 0; operations < 1000; operations++) { + queries++; + int l = Random::integer<int>(0, n+1); + int r = Random::integer<int>(0, n+1); + + ll got = st.query(l, r); + ll expected = 0; + for (int j = l; j < r; j++) expected += naive[j]; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'250'000; +void performance_test() { + timer t; + vector<ll> naive = Random::integers<ll>(N, -1000, 1000); + t.start(); + DisjointST st; + st.init(&naive); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + auto [l, r] = Random::pair<int>(0, N+1); + + t.start(); + hash += st.query(l, r); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/stlHashMap.cpp b/test/datastructures/stlHashMap.cpp new file mode 100644 index 0000000..77976fd --- /dev/null +++ b/test/datastructures/stlHashMap.cpp @@ -0,0 +1,4 @@ +#include "../util.h" +#include <datastructures/stlHashMap.cpp> + +int main() {}
\ No newline at end of file diff --git a/test/datastructures/stlTree.cpp b/test/datastructures/stlTree.cpp new file mode 100644 index 0000000..7bacbee --- /dev/null +++ b/test/datastructures/stlTree.cpp @@ -0,0 +1,2 @@ +#include "../util.h" +#include <datastructures/stlTree.cpp> diff --git a/test/datastructures/unionFind.cpp b/test/datastructures/unionFind.cpp new file mode 100644 index 0000000..2afdc86 --- /dev/null +++ b/test/datastructures/unionFind.cpp @@ -0,0 +1,109 @@ +#include "../util.h" +struct UF { + UF(int n) {init(n);} + #include <datastructures/unionFind.cpp> +}; + +struct Naive { + vector<vector<int>> adj; + vector<int> seen; + int counter; + Naive(int n) : adj(n), seen(n), counter(0) {} + + template<typename F> + void dfs(int x, F&& f) { + counter++; + vector<int> todo = {x}; + seen[x] = counter; + while (!todo.empty()) { + x = todo.back(); + todo.pop_back(); + f(x); + for (ll y : adj[x]) { + if (seen[y] != counter) { + seen[y] = counter; + todo.push_back(y); + } + } + } + } + + int findSet(int a) { + int res = a; + dfs(a, [&](int x){res = min(res, x);}); + return res; + } + + void unionSets(int a, int b) { + adj[a].push_back(b); + adj[b].push_back(a); + } + + int size(int a) { + int res = 0; + dfs(a, [&](int /**/){res++;}); + return res; + } +}; + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200; tries++) { + int n = Random::integer<int>(1, 100); + UF uf(n); + Naive naive(n); + for (int i = 0; i < n; i++) { + for (int j = 0; j < 10; j++) { + int a = Random::integer<int>(0, n); + int b = Random::integer<int>(0, n); + uf.unionSets(a, b); + naive.unionSets(a, b); + } + UF tmp = uf; + for (int j = 0; j < n; j++) { + { + auto got = tmp.size(j); + auto expected = naive.size(j); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + { + int a = Random::integer<int>(0, n); + int b = Random::integer<int>(0, n); + bool got = tmp.findSet(a) == tmp.findSet(b); + bool expected = naive.findSet(a) == naive.findSet(b); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + queries += n; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 2'000'000; +void performance_test() { + timer t; + t.start(); + UF uf(N); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + int i = Random::integer<int>(0, N); + int j = Random::integer<int>(0, N); + int k = Random::integer<int>(0, N); + int l = Random::integer<int>(0, N); + + t.start(); + uf.unionSets(i, j); + hash += uf.size(k); + hash += uf.size(l); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/datastructures/waveletTree.cpp b/test/datastructures/waveletTree.cpp new file mode 100644 index 0000000..d294835 --- /dev/null +++ b/test/datastructures/waveletTree.cpp @@ -0,0 +1,75 @@ +#include "../util.h" +#include <datastructures/waveletTree.cpp> + +constexpr int N = 1000'000; + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100; tries++) { + int n = Random::integer<int>(10, 100); + vector<ll> naive = Random::integers<ll>(n, -1000, 1000); + WaveletTree tree(naive); + for (int operations = 0; operations < 1000; operations++) { + { + queries++; + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + int x = Random::integer<int>(-1, n); + ll got = tree.kth(l, r, x); + ll expected = -1; + if (x >= 0 && l + x < r) { + vector<ll> tmp(naive.begin() + l, naive.begin() + r); + std::sort(all(tmp)); + expected = tmp[x]; + } + if (got != expected) { + cerr << "kth, got: " << got << ", expected: " << expected << FAIL; + } + } + { + queries++; + int l = Random::integer<int>(0, n + 1); + int r = Random::integer<int>(0, n + 1); + //if (l > r) swap(l, r); + ll x = Random::integer<ll>(-1000, 1000); + ll got = tree.countSmaller(l, r, x); + ll expected = 0; + for (int j = l; j < r; j++) { + if (naive[j] < x) expected++; + } + if (got != expected) { + cerr << "count, got: " << got << ", expected: " << expected << FAIL; + } + } + } + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test() { + timer t; + vector<ll> tmp = Random::integers<ll>(N, -1000, 1000); + t.start(); + WaveletTree tree(tmp); + t.stop(); + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + auto [l1, r1] = Random::pair<int>(0, N + 1); + auto [l2, r2] = Random::pair<int>(0, N + 1); + int x1 = Random::integer<ll>(l1, r1 + 1); + ll x2 = Random::integer<ll>(-1000, 1000); + + t.start(); + hash ^= tree.kth(l1, r1, x1); + hash ^= tree.countSmaller(l2, r2, x2); + t.stop(); + } + if (t.time > 2000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/geometry.h b/test/geometry.h new file mode 100644 index 0000000..7886fe2 --- /dev/null +++ b/test/geometry.h @@ -0,0 +1,140 @@ +#include <geometry/sortAround.cpp> + +namespace details { + // Liegt p auf der Strecke a-b? + bool pointInLineSegment(pt a, pt b, pt p) { + if (cross(a, b, p) != 0) return false; + auto dist = norm(a - b); + return norm(a - p) < dist && norm(b - p) < dist; + } + + // Test auf Streckenschnitt zwischen a-b und c-d. + // (nur intern) + bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { + if (cross(a, b, c) == 0 && cross(a, b, d) == 0) { + return pointInLineSegment(a,b,c) || + pointInLineSegment(a,b,d) || + pointInLineSegment(c,d,a) || + pointInLineSegment(c,d,b); + } + return ccw(a, b, c) * ccw(a, b, d) < 0 && + ccw(c, d, a) * ccw(c, d, b) < 0; + } +} + +namespace Random { + vector<ll> partition(ll n, std::size_t k){//min = 0; + n += k; + vector<ll> res = Random::distinct<ll>(k-1, 1, n); + sort(all(res)); + res.emplace_back(n); + ll last = 0; + for (std::size_t i = 0; i < k; i++) { + res[i] -= last; + last += res[i]; + res[i]--; + } + return res; + } + + vector<pt> convex(int n, ll dim) { + binomial_distribution<int> binomial(n - 2, 0.5); + + while (true) { + int left = 1 + binomial(Random::rng); + int down = 1 + binomial(Random::rng); + auto x = Random::partition(2 * dim - 2, left); + auto y = Random::partition(2 * dim - 2, down); + for (auto& z : x) z = -z; + for (auto& z : y) z = -z; + for (auto z : Random::partition(2 * dim - 2, n - left)) x.push_back(z); + for (auto z : Random::partition(2 * dim - 2, n - down)) y.push_back(z); + auto itX = std::partition(x.begin(), x.end(), [](ll z){return z == 0;}); + auto itY = std::partition(y.begin(), y.end(), [](ll z){return z != 0;}); + if (distance(x.begin(), itX) + distance(itY, y.end()) > n) continue; + shuffle(itX, x.end(), Random::rng); + if (itX != x.begin()) shuffle(y.begin(), itY, Random::rng); + + vector<pt> dirs(n); + for (size_t i = 0; i < dirs.size(); i++) { + dirs[i] = pt(x[i], y[i]); + } + sortAround(0, dirs); + + vector<pt> res = {{0, 0}}; + ll maxX = 0; + ll maxY = 0; + for (auto dir : dirs) { + pt tmp(real(res.back()) + real(dir), + imag(res.back()) + imag(dir)); + maxX = std::max<ll>(maxX, real(tmp)); + maxY = std::max<ll>(maxY, imag(tmp)); + res.emplace_back(tmp); + } + res.pop_back(); + for (auto& point : res) { + point = pt(real(point) + dim - 1 - maxX, + imag(point) + dim - 1 - maxY); + } + bool strict = true; + for (int i = 0; i < n; i++) strict &= cross(res[i], res[(i + 1) % n], res[(i + 2) % n]) != 0; + if (strict) return res; + } + } + + vector<pt> polygon(int n, ll dim) { + while (true) { + vector<pt> ps = points<pt::value_type>(n, -dim, dim); + bool coolinear = false; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + for (int k = 0; k < j; k++) { + coolinear |= cross(ps[i], ps[j], ps[k]) == 0; + } + } + } + if (coolinear) continue; + + bool changed; + do { + changed = false; + for (int i = 0; i < n && !changed; i++) { + for (int j = i + 1; j < n && !changed; j++) { + if (details::lineSegmentIntersection(ps[i], ps[(i+1) % n], ps[j], ps[(j+1) % n])) { + reverse(ps.begin() + i + 1, ps.begin() + j + 1); + changed = true; + } + } + } + } while (changed); + return ps; + } + } + + pt integerPoint(ll range) { + return pt(integer<ll>(-range, range), + integer<ll>(-range, range)); + } + + vector<pt> integerPoints(std::size_t n, ll range) { + vector<pt> res(n); + for (auto& p : res) p = integerPoint(range); + return res; + } + + array<pt, 2> line(ll range) { + pt a = integerPoint(range); + pt b = a; + while (b == a) b = integerPoint(range); + return {a, b}; + } + + array<pt, 3> triangle(ll range) { + pt a = integerPoint(range); + pt b = a; + while (b == a) b = integerPoint(range); + pt c = a; + while (ccw(a, b, c) == 0) c = integerPoint(range); + return {a, b, c}; + } +}
\ No newline at end of file diff --git a/test/geometry/antipodalPoints.cpp b/test/geometry/antipodalPoints.cpp new file mode 100644 index 0000000..d20dfb6 --- /dev/null +++ b/test/geometry/antipodalPoints.cpp @@ -0,0 +1,70 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double +#include <geometry/antipodalPoints.cpp> +#include "../geometry.h" + +vector<pair<int, int>> naive(vector<pt> ps) { + ll n = sz(ps); + auto test = [&](int i, int j){ + if (dot(ps[j] - ps[i], ps[i - 1] - ps[i]) <= 0) return false; + if (dot(ps[j] - ps[i], ps[i + 1] - ps[i]) <= 0) return false; + return true; + }; + ps.push_back(ps[0]); + ps.push_back(ps[1]); + vector<pair<int, int>> res; + for (ll i = 1; i <= n; i++) { + for (ll j = 1; j < i; j++) { + if (test(i, j) && test(j, i)) res.emplace_back(i % n, j % n); + } + } + return res; +} + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(3, 30); + auto ps = Random::convex(n, range); + + auto got = antipodalPoints(ps); + for (auto& [a, b] : got) if (a > b) swap(a, b); + sort(all(got)); + + auto expected = naive(ps); + for (auto& [a, b] : expected) if (a > b) swap(a, b); + + for (auto x : expected) { + auto it = lower_bound(all(got), x); + if (it == got.end() || *it != x) cerr << "error" << FAIL; + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 99'000; +void performance_test() { + timer t; + + auto ps = Random::convex(N, 1'000'000'000); + + t.start(); + auto got = antipodalPoints(ps); + t.stop(); + + hash_t hash = sz(got); + if (t.time > 50) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/circle.cpp b/test/geometry/circle.cpp new file mode 100644 index 0000000..3d3d27d --- /dev/null +++ b/test/geometry/circle.cpp @@ -0,0 +1,116 @@ +#include "../util.h" +constexpr double EPS = 1e-6; +#define ll double +double gcd(double x, double /**/) {return x;} //hacky +#include <geometry/formulas.cpp> +#undef ll +#include <geometry/circle.cpp> + +// Entfernung von Punkt p zur Geraden durch a-b. 2d und 3d +double distToLine(pt a, pt b, pt p) { + return abs(cross(p - a, b - a)) / abs(b - a); +} + +pt randomIntegerPT(ll range) { + return pt(Random::integer<ll>(-range, range), Random::integer<ll>(-range, range)); +} + +ll sq(ll x) { + return x*x; +} + +int expectedCount(ll x1, ll y1, ll r1, ll x2, ll y2, ll r2) { + if (x1 == x2 && y1 == y2){ + return r1 == r2 ? -1 : 0; + } else { + ll d = sq(x1 - x2) + sq(y1 - y2); + + if (d > sq(r1 + r2) || d < sq(r1 - r2)) { + return 0; + } else if (d == sq(r1 + r2) || d == sq(r1 - r2)) { + return 1; + } else{ + return 2; + } + } +} + +void test_circleIntersection(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto c1 = randomIntegerPT(range); + auto c2 = c1; + while (c1 == c2) c2 = randomIntegerPT(range); + double r1 = Random::integer<ll>(1, range); + double r2 = Random::integer<ll>(1, range); + + auto got = circleIntersection(c1, r1, c2, r2); + + if (sz(got) != expectedCount(real(c1), imag(c1), r1, real(c2), imag(c2), r2)) cerr << "error: wrong count" << FAIL; + + for (int i = 0; i < sz(got); i++) { + for (int j = 0; j < i; j++) { + if (abs(got[i] - got[j]) < 1e-6) cerr << "error: identical" << FAIL; + } + } + + for (auto p : got) { + if (float_error(abs(c1 - p), r1) > 1e-6) cerr << "error: 1" << FAIL; + if (float_error(abs(c2 - p), r2) > 1e-6) cerr << "error: 2" << FAIL; + } + queries += sz(got); + } + cerr << "tested circleIntersection: " << queries << endl; +} + +void test_circleRayIntersection(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto c = randomIntegerPT(range); + double r = Random::integer<ll>(1, range); + + pt orig = randomIntegerPT(range); + pt dir = 0; + while (abs(dir) < 0.5) dir = randomIntegerPT(range); + + auto got = circleRayIntersection(c, r, orig, dir); + + double dist = distToLine(orig, orig + dir, c); + int lineIntersections = 0; + if (dist <= r) lineIntersections = 2; + if (abs(dist - r) < 1e-9) lineIntersections = 1; + + int expected = 0; + if (abs(orig - c) < r) expected = 1; //starts inside + if (abs(orig - c) > r) { //starts outside + if (dot(dir, c - orig) >= 0) expected = lineIntersections; + else expected = 0; + } + if (abs(abs(orig - c) - r) < 1e-9) { //starts on circle + if (dot(dir, c - orig) >= 0) expected = lineIntersections; + else expected = 1; + } + + if (sz(got) != expected) cerr << "error: wrong count" << FAIL; + + for (int i = 0; i < sz(got); i++) { + for (int j = 0; j < i; j++) { + if (abs(got[i] - got[j]) < 1e-6) cerr << "error: identical" << FAIL; + } + } + + for (auto p : got) { + if (float_error(abs(c - p), r) > 1e-6) cerr << "error: 1" << FAIL; + if (distToLine(orig, orig + dir, p) > 1e-6) cerr << "error: 2" << FAIL; + } + queries += sz(got); + } + cerr << "tested circleIntersection: " << queries << endl; +} + +int main() { + test_circleIntersection(10); + test_circleIntersection(100); + test_circleRayIntersection(10); + test_circleRayIntersection(100); +} diff --git a/test/geometry/closestPair.cpp b/test/geometry/closestPair.cpp new file mode 100644 index 0000000..5959b21 --- /dev/null +++ b/test/geometry/closestPair.cpp @@ -0,0 +1,69 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double +constexpr ll INF = LL::INF; +ll sq(ll x) {return x*x;} +ll isqrt(ll x) {return (ll)sqrtl(x);} +#include <geometry/closestPair.cpp> + +//strict convex hull +ll naive(const vector<pt>& ps) { + ll opt = LL::INF; + for (ll i = 0; i < sz(ps); i++) { + for (ll j = 0; j < i; j++) { + opt = min(opt, norm(ps[i] - ps[j])); + } + } + return opt; +} + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 100); + auto ps = Random::points<ll>(n, -range, range); + auto got = shortestDist(ps); + auto expected = naive(ps); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + double maxTime = 0; + + vector<pt> ps; + for (int i = 0; i*i <= N; i++) { + for (int j = 0; j*j <= N; j++) { + ps.emplace_back(i, j); + } + } + t.start(); + hash = shortestDist(ps); + t.stop(); + maxTime = max(maxTime, t.time); + + ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000); + t.reset(); + t.start(); + hash += shortestDist(ps); + t.stop(); + maxTime = max(maxTime, t.time); + + if (maxTime > 500) cerr << "too slow: " << maxTime << FAIL; + cerr << "tested performance: " << maxTime << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/closestPair.double.cpp b/test/geometry/closestPair.double.cpp new file mode 100644 index 0000000..2f8a1ab --- /dev/null +++ b/test/geometry/closestPair.double.cpp @@ -0,0 +1,66 @@ +#include "../util.h" +constexpr double EPS = 1e-9; +#define ll double +double gcd(double x, double /**/) {return x;} //hacky +#include <geometry/formulas.cpp> +constexpr ll INF = LL::INF; +#include <geometry/closestPair.cpp> +#undef ll + +//strict convex hull +double naive(const vector<pt>& ps) { + double opt = LL::INF; + for (ll i = 0; i < sz(ps); i++) { + for (ll j = 0; j < i; j++) { + opt = min(opt, norm(ps[i] - ps[j])); + } + } + return opt; +} + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 100); + auto ps = Random::points<double>(n, -range, range); + auto got = shortestDist(ps); + auto expected = naive(ps); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + double maxTime = 0; + + vector<pt> ps; + for (int i = 0; i*i <= N; i++) { + for (int j = 0; j*j <= N; j++) { + ps.emplace_back(i, j); + } + } + t.start(); + hash = shortestDist(ps); + t.stop(); + maxTime = max(maxTime, t.time); + + ps = Random::points<double>(N, -1'000'000'000, 1'000'000'000); + t.reset(); + t.start(); + hash += shortestDist(ps); + t.stop(); + maxTime = max(maxTime, t.time); + + if (maxTime > 500) cerr << "too slow: " << maxTime << FAIL; + cerr << "tested performance: " << maxTime << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/convexHull.cpp b/test/geometry/convexHull.cpp new file mode 100644 index 0000000..788a634 --- /dev/null +++ b/test/geometry/convexHull.cpp @@ -0,0 +1,79 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double +#include <geometry/convexHull.cpp> + +//strict convex hull +ll isConvexHull(const vector<pt>& ps, const vector<pt>& hull) { + ll n = sz(hull) - 1; + if (n == 0) { + for (pt p : ps) if (p != hull[0]) return 1; + return 0; + } else { + if (hull[0] != hull[n]) return 2; + //hull has no duplicates + for (ll i = 0; i < n; i++) { + for (ll j = 0; j < i; j++) { + if (hull[i] == hull[j]) return 3; + } + } + //hull is subset + for (pt p : hull) { + bool isP = false; + for (pt c : ps) isP |= c == p; + if (!isP) return 4; + } + //hull contains all points + for (pt p : hull) { + ll mi = 1; + for (ll i = 0; i < n; i++) { + mi = min(mi, cross(hull[i], hull[i + 1], p)); + } + if (mi < 0) return 5; //outside + if (mi > 0) continue; + bool isCorner = 4; + for (pt c : hull) isCorner |= c == p; + if (!isCorner) return 6; + } + // hull is convex + if (n <= 2) return 0; + for (ll i = 0; i < n; i++) { + if (cross(hull[i], hull[i + 1], hull[(i + 2) % n]) <= 0) return 7; + } + return 0; + } +} + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 100); + auto ps = Random::points<ll>(n, -range, range); + auto got = convexHull(ps); + if (isConvexHull(ps, got) > 0) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 2'000'000; +void performance_test() { + timer t; + auto ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000); + t.start(); + auto a = convexHull(ps); + t.stop(); + hash_t hash = sz(a); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/delaunay.cpp b/test/geometry/delaunay.cpp new file mode 100644 index 0000000..7f8ec30 --- /dev/null +++ b/test/geometry/delaunay.cpp @@ -0,0 +1,144 @@ +#include "../util.h" +using pt = complex<lll>; +// Kreuzprodukt, 0, falls kollinear. +auto cross(pt a, pt b) {return imag(conj(a) * b);} +auto cross(pt p, pt a, pt b) {return cross(a - p, b - p);} +#pragma GCC diagnostic ignored "-Wunused-variable" +#include <geometry/delaunay.cpp> + +vector<pt> convexHull(vector<pt> pts){ + sort(all(pts), [](const pt& a, const pt& b){ + return real(a) == real(b) ? imag(a) < imag(b) + : real(a) < real(b); + }); + pts.erase(unique(all(pts)), pts.end()); + int k = 0; + vector<pt> h(2 * sz(pts)); + auto half = [&](auto begin, auto end, int t) { + for (auto it = begin; it != end; it++) { + while (k > t && cross(h[k-2], h[k-1], *it) < 0) k--;//allow collinear points! + h[k++] = *it; + }}; + half(all(pts), 1);// Untere Hülle. + half(next(pts.rbegin()), pts.rend(), k);// Obere Hülle. + h.resize(k); + return h; +} + +lll area(const vector<pt>& poly) { //poly[0] == poly.back() + lll res = 0; + for (int i = 0; i + 1 < sz(poly); i++) + res += cross(poly[i], poly[i + 1]); + return res; +} + +// Liegt p auf der Strecke a-b? +bool pointInLineSegment(pt a, pt b, pt p) { + if (cross(a, b, p) != 0) return false; + auto dist = norm(a - b); + return norm(a - p) < dist && norm(b - p) < dist; +} + +// Test auf Streckenschnitt zwischen a-b und c-d. +// (nur intern) +bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { + if (cross(a, b, c) == 0 && cross(a, b, d) == 0) { + return pointInLineSegment(a,b,c) || + pointInLineSegment(a,b,d) || + pointInLineSegment(c,d,a) || + pointInLineSegment(c,d,b); + } + return cross(a, b, c) * cross(a, b, d) < 0 && + cross(c, d, a) * cross(c, d, b) < 0; +} + +// 1 => c links von a->b +// 0 => a, b und c kolliniear +// -1 => c rechts von a->b +int ccw(pt a, pt b, pt c) { + auto orien = cross(b - a, c - a); + return (orien > 0) - (orien < 0); +} + +bool inOutCirc(pt a, pt b, pt c, pt p) { + lll p2 = norm(p); + lll A = norm(a)-p2; + lll B = norm(b)-p2; + lll C = norm(c)-p2; + return ccw(a, b, c) * (cross(p, a, b)*C + cross(p, b, c)*A + cross(p, c, a)*B) > 0; +} + + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(3, 30); + auto ps = Random::points<lll>(n, -range, range); + bool skip = true; + for (int i = 2; i < n; i++) skip &= cross(ps[i-2], ps[i-1], ps[i]) == 0; + if (skip) continue; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + skip |= ps[i] == ps[j]; + } + } + if (skip) continue; + + auto hull = convexHull(ps); + lll expectedArea = area(hull); + hull.pop_back(); + + auto got = delaunay(ps); + if (sz(got) % 3 != 0) cerr << "error: not triangles" << FAIL; + if (sz(got) / 3 + sz(hull) - 3 + 1 != 2 * sz(ps) - 4) cerr << "error: wrong number" << FAIL; + + //all triangles should be oriented ccw + lll gotArea = 0; + for (int i = 0; i < sz(got); i += 3) gotArea += cross(got[i], got[i+1], got[i+2]); + if (gotArea != expectedArea) cerr << "error: wrong area" << FAIL; + + for (int i = 0; i < sz(got); i++) { + int ii = i + 1; + if (i / 3 != ii / 3) ii -= 3; + for (int j = 0; j < i; j++) { + int jj = j + 1; + if (j / 3 != jj / 3) jj -= 3; + + if (got[i] == got[j] && got[ii] == got[jj]) cerr << "error: dublicate" << FAIL; + if (lineSegmentIntersection(got[i], got[ii], got[j], got[jj])) cerr << "error: intersection" << FAIL; + } + bool seen = false; + for (pt p : ps) seen |= p == got[i]; + if (!seen) cerr << "error: invalid point" << FAIL; + } + for (int i = 0; i < sz(got); i += 3) { + for (pt p : ps) { + if (p == got[i]) continue; + if (p == got[i+1]) continue; + if (p == got[i+2]) continue; + if (inOutCirc(got[i], got[i+1], got[i+2], p)) cerr << "error: not delaunay" << FAIL; + } + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 100'000; +void performance_test() { + timer t; + auto ps = Random::points<lll>(N, -1'000'000'000, 1'000'000'000); + t.start(); + auto got = delaunay(ps); + t.stop(); + hash_t hash = sz(got); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(10); + stress_test(10'000); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/formulas.cpp b/test/geometry/formulas.cpp new file mode 100644 index 0000000..d63d431 --- /dev/null +++ b/test/geometry/formulas.cpp @@ -0,0 +1,127 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double + +void test_dot(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto p = Random::point<ll>(-range, range); + auto q = Random::point<ll>(-range, range); + + ll expected = real(p) * real(q) + imag(p) * imag(q); + ll got = dot(p, q); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested dot: " << queries << endl; +} + +void test_norm(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto p = Random::point<ll>(-range, range); + + ll expected = real(p) * real(p) + imag(p) * imag(p); + ll got = norm(p); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested norm: " << queries << endl; +} + +void test_cross(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto p = Random::point<ll>(-range, range); + auto q = Random::point<ll>(-range, range); + + ll expected = real(p) * imag(q) - imag(p) * real(q); + ll got = cross(p, q); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested cross1: " << queries << endl; + + queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto a = Random::point<ll>(-range, range); + auto b = Random::point<ll>(-range, range); + auto c = Random::point<ll>(-range, range); + + ll expected = cross(b - a, c - a); + ll got = cross(a, b, c); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested cross2: " << queries << endl; +} + +void test_ccw(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto a = Random::point<ll>(-range, range); + auto b = Random::point<ll>(-range, range); + auto c = Random::point<ll>(-range, range); + + ll expected = cross(a, b, c); + if (expected < 0) expected = -1; + if (expected > 0) expected = 1; + ll got = ccw(a, b, c); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested ccw: " << queries << endl; +} + +void test_isCoplanar(ll range) {(void) range;}// cant check this... + +void test_uniqueAngle(ll range) { + auto lessPt = [](pt a, pt b){ + if (real(a) != real(b)) return real(a) < real(b); + return imag(a) < imag(b); + }; + map<pt, pt, decltype(lessPt)> seen(lessPt); + + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + pt expected = Random::point<ll>(-sqrt(range), sqrt(range)); + ll g = abs(gcd(real(expected), imag(expected))); + if (g == 0) continue; + expected /= g; + + pt rot = Random::point<ll>(-sqrt(range), sqrt(range)); + if (norm(rot) == 0) continue; + + pt got = uniqueAngle(expected * rot, pt(Random::integer<ll>(1, sqrt(range)), 0) * rot); + auto it = seen.emplace(got, expected).first; + + if (it->second != expected) cerr << "error: inconsistent" << FAIL; + queries++; + } + cerr << "tested uniqueAngle: " << queries << " (" << sz(seen) << ")" << endl; +} + +int main() { + test_dot(100); + test_dot(1'000'000'000); + test_norm(100); + test_norm(1'000'000'000); + test_cross(100); + test_cross(1'000'000'000); + test_ccw(100); + test_ccw(1'000'000'000); + test_isCoplanar(100); + test_isCoplanar(1'000'000'000); + test_uniqueAngle(100); + test_uniqueAngle(10'000); + test_uniqueAngle(1'000'000'000); +} diff --git a/test/geometry/linesAndSegments.cpp b/test/geometry/linesAndSegments.cpp new file mode 100644 index 0000000..2943a67 --- /dev/null +++ b/test/geometry/linesAndSegments.cpp @@ -0,0 +1,240 @@ +#include "../util.h" +constexpr double EPS = 1e-9; +#define ll double +double gcd(double x, double /**/) {return x;} //hacky +#include <geometry/formulas.cpp> +#undef ll +#include <geometry/linesAndSegments.cpp> + +#include "../geometry.h" + +void stress_pointOnLine(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + pt p = Random::integerPoint(range); + + bool expected = ccw(a, b, p) == 0; + bool got = pointOnLine(a, b, p); + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + cerr << "tested pointOnLine: " << queries << endl; +} + +void stress_lineIntersection(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + auto [c, d] = Random::line(range); + if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) continue; + + bool expected = ccw(0, a-b, c-d) == 0; + bool got = lineIntersection(a, b, c, d); + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + cerr << "tested lineIntersection: " << queries << endl; +} + +void stress_lineIntersection2(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + auto [c, d] = Random::line(range); + if (ccw(0, a-b, c-d) == 0) continue; + + auto got = lineIntersection2(a, b, c, d); + + if (distToLine(a, b, got) > 1e-6) cerr << "error: 1" << FAIL; + if (distToLine(a, b, got) > 1e-6) cerr << "error: 2" << FAIL; + queries++; + } + cerr << "tested lineIntersection2: " << queries << endl; +} + +void stress_distToLine(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + pt p = Random::integerPoint(range); + + auto got = distToLine(a, b, p); + auto expected = abs(p - projectToLine(a, b, p)); + + if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL; + + queries++; + } + cerr << "tested distToLine: " << queries << endl; +} + +void stress_projectToLine(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + pt p = Random::integerPoint(range); + + auto got = projectToLine(a, b, p); + + if (distToLine(a, b, got) > 1e-6) cerr << "error: 1" << FAIL; + if (dot((b-a)/abs(b-a), (got-p)/abs(got-p)) > 1e-6) cerr << "error: 2" << FAIL; + + queries++; + } + cerr << "tested projectToLine: " << queries << endl; +} + +void stress_sortLine(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + pt dir = 0; + while (norm(dir) == 0) dir = Random::integerPoint(range); + int n = Random::integer<int>(1, 30); + vector<pt> ps = Random::integerPoints(n, range); + + sortLine(dir, ps); + + for (int i = 1; i < n; i++) { + if (dot(dir, ps[i-1]) > dot(dir, ps[i])) cerr << "error" << FAIL; + } + queries+=n; + } + cerr << "tested sortLine: " << queries << endl; +} + +void stress_pointOnSegment(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + pt p = Random::integerPoint(range); + + bool expected = pointOnLine(a, b, p) && abs(a-p) <= abs(a-b) && abs(b-p) <= abs(a-b); + bool got = pointOnSegment(a, b, p); + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + cerr << "tested pointOnSegment: " << queries << endl; +} + +void stress_distToSegment(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + pt p = Random::integerPoint(range); + + double expected = min(abs(p-a), abs(p-b)); + if (dot(b-a,p-a) >= 0 && dot(a-b,p-b) >= 0) expected = min(expected, distToLine(a, b, p)); + double got = distToSegment(a, b, p); + + if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL; + queries++; + } + cerr << "tested distToSegment: " << queries << endl; +} + +void stress_segmentIntersection(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + auto [c, d] = Random::line(range); + + bool expected; + if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) { + expected = pointOnSegment(a,b,c) || + pointOnSegment(a,b,d) || + pointOnSegment(c,d,a) || + pointOnSegment(c,d,b); + } else { + expected = ccw(a, b, c) * ccw(a, b, d) <= 0 && + ccw(c, d, a) * ccw(c, d, b) <= 0; + } + bool got = segmentIntersection(a, b, c, d); + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + cerr << "tested segmentIntersection: " << queries << endl; +} + +void stress_segmentIntersection2(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + auto [c, d] = Random::line(range); + + auto got = segmentIntersection2(a, b, c, d); + auto tmp = segmentIntersection(a, b, c, d); + + if (!got.empty() != tmp) cerr << "error: 1" << FAIL; + for (pt p : got) { + if (distToSegment(a, b, p) > 1e-6) cerr << "error: 2" << FAIL; + if (distToSegment(a, b, p) > 1e-6) cerr << "error: 3" << FAIL; + } + if (tmp) { + double gotDist = abs(got.front() - got.back()); + double expectedDist = 0; + array<pt, 4> tmp2 = {a, b, c, d}; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < i; j++) { + if (!pointOnSegment(a, b, tmp2[i])) continue; + if (!pointOnSegment(c, d, tmp2[i])) continue; + if (!pointOnSegment(a, b, tmp2[j])) continue; + if (!pointOnSegment(c, d, tmp2[j])) continue; + expectedDist = max(expectedDist, abs(tmp2[i] - tmp2[j])); + } + } + if (float_error(gotDist, expectedDist) > 1e-6) cerr << "error: 4" << FAIL; + } + queries++; + } + cerr << "tested segmentIntersection2: " << queries << endl; +} + +void stress_distBetweenSegments(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + auto [a, b] = Random::line(range); + auto [c, d] = Random::line(range); + + double expected = 0; + if (!segmentIntersection(a, b, c, d)) { + expected = min({distToSegment(a, b, c), distToSegment(a, b, d), + distToSegment(c, d, a), distToSegment(c, d, b)}); + } + double got = distBetweenSegments(a, b, c, d); + + if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL; + queries++; + } + cerr << "tested distBetweenSegments: " << queries << endl; +} + +int main() { + stress_pointOnLine(100); + stress_pointOnLine(10'000); + stress_pointOnLine(1'000'000'000); + stress_lineIntersection(100); + stress_lineIntersection(1'000'000'000); + stress_lineIntersection2(100); + stress_lineIntersection2(1'000'000); + stress_distToLine(100); + stress_distToLine(1'000'000'000); + stress_projectToLine(100); + stress_projectToLine(1'000'000); + stress_sortLine(100); + stress_sortLine(1'000'000'000); + stress_pointOnSegment(100); + stress_pointOnSegment(1'000'000'000); + stress_distToSegment(100); + stress_distToSegment(1'000'000'000); + stress_segmentIntersection(100); + stress_segmentIntersection(1'000'000'000); + stress_segmentIntersection2(100); + stress_segmentIntersection2(1'000'000'000); + stress_distBetweenSegments(100); + stress_distBetweenSegments(1'000'000'000); +} diff --git a/test/geometry/polygon.cpp b/test/geometry/polygon.cpp new file mode 100644 index 0000000..1dd46ca --- /dev/null +++ b/test/geometry/polygon.cpp @@ -0,0 +1,296 @@ +#include "../util.h" +constexpr ll EPS = 0; +constexpr double INF = LD::INF; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double +double abs(pt p) { + return hypot(real(p), imag(p)); +} +// Liegt p auf der Strecke a-b? +bool pointOnLineSegment(pt a, pt b, pt p) { + if (cross(a, b, p) != 0) return false; + auto dist = norm(a - b); + return norm(a - p) <= dist && norm(b - p) <= dist; +} +// Entfernung von Punkt p zur Strecke a-b. +double distToSegment(pt a, pt b, pt p) { + if (a == b) return abs(p - a); + if (dot(p - a, b - a) <= 0) return abs(p - a); + if (dot(p - b, b - a) >= 0) return abs(p - b); + return abs(cross(p - a, b - a)) / abs(b - a); +} +#pragma GCC diagnostic ignored "-Wunused-variable" +#include <geometry/polygon.cpp> +#include "../geometry.h" + +void test_area(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 30); + auto ps = Random::polygon(n, range); + ps.push_back(ps[0]); + + ll expected = 0; + for (int i = 0; i < n; i++) { + expected += cross(0, ps[i], ps[i+1]); + } + double got = area(ps) * 2; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested area: " << queries << endl; +} + +bool ptLess(pt a, pt b) { + if (real(a) != real(b)) return real(a) < real(b); + return imag(a) < imag(b); +} + +void test_windingNumber(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 8); + auto ps = Random::polygon(n, range); + ps.push_back(ps[0]); + + for (int i = 0; i < 100; i++) { + auto p = Random::point<ll>(-range, range); + + ll expected = 0; + bool onBorder = false; + for (int j = 0; j < n; j++) { + int cur = details::lineSegmentIntersection(p, p + pt(1, 2'000'000'007), ps[j], ps[j+1]); + if (ptLess(ps[j], ps[j+1])) expected -= cur; + else expected += cur; + onBorder |= pointOnLineSegment(ps[j], ps[j+1], p); + } + if (onBorder) continue; + if (area(ps) < 0) expected = -expected; + + bool got = windingNumber(p, ps); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + queries += n; + } + cerr << "tested windingNumber: " << queries << endl; +} + +void test_inside(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 30); + auto ps = Random::polygon(n, range); + ps.push_back(ps[0]); + + for (int i = 0; i < 100; i++) { + auto p = Random::point<ll>(-range, range); + + ll count = 0; + bool onBorder = false; + for (int j = 0; j < n; j++) { + count += details::lineSegmentIntersection(p, p + pt(1, 2'000'000'007), ps[j], ps[j+1]); + onBorder |= pointOnLineSegment(ps[j], ps[j+1], p); + } + bool expected = (count % 2) && !onBorder; + bool got = inside(p, ps); + + if (got != expected) cerr << "error" << FAIL; + } + queries += n; + } + cerr << "tested inside: " << queries << endl; +} + +void test_insideConvex(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 30); + auto ps = Random::convex(n, range); + + for (int i = 0; i < 100; i++) { + auto p = Random::point<ll>(-range, range); + + bool expected = true; + for (int j = 0; j < n; j++) expected &= cross(p, ps[j], ps[(j+1) % n]) > 0; + + bool got = insideConvex(p, ps); + + if (got != expected) { + for (pt pp : ps) cerr << pp << " "; + cerr << endl; + cerr << p << endl; + } + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + queries += n; + } + cerr << "tested insideConvex: " << queries << endl; +} + +// convex hull without duplicates, h[0] != h.back() +// apply comments if border counts as inside +bool insideOrOnConvex(pt p, const vector<pt>& hull) { + int l = 0, r = sz(hull) - 1; + if (cross(hull[0], hull[r], p) > 0) return false; + while (l + 1 < r) { + int m = (l + r) / 2; + if (cross(hull[0], hull[m], p) >= 0) l = m; + else r = m; + } + return cross(hull[l], hull[r], p) >= 0; +} + +void test_minkowski(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 30); + auto A = Random::convex(n, range); + int m = Random::integer(3, 30); + auto B = Random::convex(n, range); + + auto got = minkowski(A, B); + bool convex = true; + for (int i = 0; i < sz(got); i++) convex &= cross(got[i], got[(i+1) % sz(got)], got[(i+2) % sz(got)]) >= 0; + if (!convex) cerr << "error: not convex" << FAIL; + + for (pt a : A) { + for (pt b : B) { + if (!insideOrOnConvex(a + b, got)) cerr << "error: not sum" << FAIL; + } + } + queries += n + m; + } + cerr << "tested minkowski: " << queries << endl; +} + +double naive_dist(const vector<pt>& ps, const vector<pt>& qs) { + //check if intersect + double res = LD::INF; + bool intersect = true; + for (int i = 0; i < sz(qs); i++) { + bool sep = true; + for (pt p : ps) { + res = min(res, distToSegment(qs[i], qs[(i+1) % sz(qs)], p)); + sep &= cross(qs[i], qs[(i+1) % sz(qs)], p) <= 0; + } + if (sep) intersect = false; + } + for (int i = 0; i < sz(ps); i++) { + bool sep = true; + for (pt q : qs) { + res = min(res, distToSegment(ps[i], ps[(i+1) % sz(ps)], q)); + sep &= cross(ps[i], ps[(i+1) % sz(ps)], q) <= 0; + } + if (sep) intersect = false; + } + if (intersect) return 0; + return res; +} + +void test_dist(ll range) { + int queries = 0; + int pos = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 10); + auto A = Random::convex(n, range / 3); + int m = Random::integer(3, 10); + auto B = Random::convex(n, range / 3); + + pt offset = Random::point<ll>(range / 3, 2 * range / 3); + for (pt& p : B) p += offset; + + auto got = dist(A, B); + auto expected = naive_dist(A, B); + + if (float_error(got, expected) > 1e-6) cerr << "got: " << got << ", expected: " << expected << FAIL; + if (got > 0) pos++; + + queries += n + m; + } + cerr << "tested dist: " << queries << " (" << pos << ")" << endl; +} + +void test_extremal(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 30); + auto ps = Random::convex(n, range); + ps.push_back(ps[0]); + + for (int i = 0; i < 100; i++) { + auto dir = Random::point<ll>(-range, range); + int tmp = extremal(ps, dir); + if (tmp < 0 || tmp >= n) cerr << "error: out of range" << FAIL; + + auto got = ps[tmp]; + bool extremal = true; + for (pt p : ps) extremal &= dot(dir, p) <= dot(dir, got); + + if (!extremal) cerr << "error: not extremal" << FAIL; + queries += n; + } + } + cerr << "tested extremal: " << queries << endl; +} + +void test_intersect(ll range) { + int queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer(3, 10); + auto ps = Random::convex(n, range); + ps.push_back(ps[0]); + + for (int i = 0; i < 100; i++) { + pt a = Random::point<ll>(-range, range); + pt b = a; + while (b == a) b = Random::point<ll>(-range, range); + + auto got = intersectLine(ps, a, b); + + vector<int> expected; + for (int j = 0; j < n; j++) { + if (cross(ps[j], a, b) > 0 && cross(ps[j+1], a, b) < 0) expected.push_back(j); + if (cross(ps[j], a, b) < 0 && cross(ps[j+1], a, b) > 0) expected.push_back(j); + if (cross(ps[j], a, b) == 0) { + if (cross(ps[j+1], a, b) != 0 || + cross(ps[(j+n-1) % n], a, b) != 0) { + expected.push_back(j); + } + } + } + if (sz(expected) > 1 && expected[0] == expected[1]) expected.pop_back(); + + sort(all(got)); + sort(all(expected)); + + if (got != expected) cerr << "error" << FAIL; + + queries += n; + } + } + cerr << "tested intersect: " << queries << endl; +} + +int main() { + test_area(100); + test_area(1'000'000'000); + test_windingNumber(100); + test_windingNumber(1'000'000'000); + test_inside(100); + test_inside(1'000'000'000); + test_insideConvex(100); + test_insideConvex(1'000'000'000); + test_minkowski(100); + test_minkowski(500'000'000); + test_dist(100); + test_dist(1'000'000'000); + test_extremal(100); + test_extremal(1'000'000'000); + test_intersect(100); + test_intersect(1'000'000'000); +} diff --git a/test/geometry/segmentIntersection.cpp b/test/geometry/segmentIntersection.cpp new file mode 100644 index 0000000..9862be5 --- /dev/null +++ b/test/geometry/segmentIntersection.cpp @@ -0,0 +1,88 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double + +// Liegt p auf der Strecke a-b? +bool pointOnLineSegment(pt a, pt b, pt p) { + if (cross(a, b, p) != 0) return false; + double dist = norm(a - b); + return norm(a - p) <= dist && norm(b - p) <= dist; +} + +// Test auf Streckenschnitt zwischen a-b und c-d. +bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { + if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) + return pointOnLineSegment(a,b,c) || + pointOnLineSegment(a,b,d) || + pointOnLineSegment(c,d,a) || + pointOnLineSegment(c,d,b); + return ccw(a, b, c) * ccw(a, b, d) <= 0 && + ccw(c, d, a) * ccw(c, d, b) <= 0; +} + +#include <geometry/segmentIntersection.cpp> + +vector<seg> randomSegs(int n, ll range) { + auto ps = Random::points<ll>(n, -range, range); + vector<seg> segs(n); + for (int i = 0; i < n; i++) { + pt b; + do { + b = Random::point<ll>(-pow(range, 0.8), pow(range, 0.8)); + } while(norm(b) == 0); + segs[i] = {ps[i], ps[i] + b, i}; + } + return segs; +} + +bool naive(vector<seg>& segs) { + for (ll i = 0; i < sz(segs); i++) { + for (ll j = 0; j < i; j++) { + if (lineSegmentIntersection(segs[i].a, segs[i].b, segs[j].a, segs[j].b)) return true; + } + } + return false; +} + +void stress_test(ll range) { + ll queries = 0; + ll intersection = 0; + ll notIntersection = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 100); + auto segs = randomSegs(n, range); + auto [a, b] = intersect(segs); + bool got = a >= 0; + if (got != (b >= 0)) cerr << "error: invalid ans" << FAIL; + auto expected = naive(segs); + if (got != expected) cerr << "error: intersection not found" << FAIL; + if (got && !lineSegmentIntersection(segs[a].a, segs[a].b, segs[b].a, segs[b].b)) cerr << "error: no intersection" << FAIL; + queries += n; + intersection += got; + notIntersection += !got; + } + cerr << "tested random queries: " << queries << "(" << intersection << ":" << notIntersection << ")" << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + auto segs = randomSegs(N, 1'000'000'000); + + t.start(); + hash_t hash = intersect(segs).first; + t.stop(); + + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/sortAround.cpp b/test/geometry/sortAround.cpp new file mode 100644 index 0000000..a27edc8 --- /dev/null +++ b/test/geometry/sortAround.cpp @@ -0,0 +1,83 @@ +#include "../util.h" +constexpr ll EPS = 0; +#define double ll +#define polar polar<ll> +#include <geometry/formulas.cpp> +#undef polar +#undef double +#include <geometry/sortAround.cpp> + +//expected order: +//1 8 7 +//2 . 6 +//3 4 5 +void test_tiny() { + vector<pt> expected = { + {-1, 1}, + {-1, 0}, + {-1,-1}, + { 0,-1}, + { 1,-1}, + { 1, 0}, + { 1, 1}, + { 0, 1}, + }; + auto got = expected; + for (int i = 0; i < 100'000; i++) { + shuffle(all(got), Random::rng); + sortAround(0, got); + if (got != expected) cerr << "error" << FAIL; + } + cerr << "tested tiny" << endl; +} + +void stress_test(ll range) { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 100); + auto ps = Random::points<ll>(n, -range, range); + + auto contains = [&](pt p){ + for (pt pp : ps) if (pp == p) return true; + return false; + }; + + pt c; + do { + c = Random::point<ll>(-range, range); + } while (contains(c)); + + sortAround(c, ps); + + auto isLeft = [&](pt p){return real(p - c) < 0 || (real(p - c) == 0 && imag(p - c) < 0);}; + auto isCCW = [&](pt a, pt b){return cross(c, a, b) > 0;}; + if (!is_partitioned(all(ps), isLeft)) cerr << "error 1" << FAIL; + auto mid = partition_point(all(ps), isLeft); + if (!is_sorted(ps.begin(), mid, isCCW)) cerr << "error 2" << FAIL; + if (!is_sorted(mid, ps.end(), isCCW)) cerr << "error 3" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 2'000'000; +void performance_test() { + timer t; + auto ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000); + + t.start(); + sortAround(0, ps); + t.stop(); + + hash_t hash = 0; + for (pt p : ps) hash += real(p) * imag(p); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + test_tiny(); + stress_test(100); + stress_test(1'000'000'000); + performance_test(); +} diff --git a/test/geometry/triangle.cpp b/test/geometry/triangle.cpp new file mode 100644 index 0000000..dc620ee --- /dev/null +++ b/test/geometry/triangle.cpp @@ -0,0 +1,146 @@ +#include "../util.h" +constexpr double EPS = 1e-6; +#define ll double +double gcd(double x, double /**/) {return x;} //hacky +#include <geometry/formulas.cpp> +#undef ll +ll sgn(double x) { + return (x > EPS) - (x < -EPS); +} +#include <geometry/triangle.cpp> +#include "../geometry.h" + +// Entfernung von Punkt p zur Geraden durch a-b. 2d und 3d +double distToLine(pt a, pt b, pt p) { + return abs(cross(p - a, b - a)) / abs(b - a); +} + +void test_centroid(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(range); + + pt center = centroid(a, b, c); + + if (distToLine(2.0*a, c+b, 2.0*center) > 1e-6) cerr << "error: 1" << FAIL; + if (distToLine(2.0*b, c+a, 2.0*center) > 1e-6) cerr << "error: 2" << FAIL; + if (distToLine(2.0*c, a+b, 2.0*center) > 1e-6) cerr << "error: 3" << FAIL; + queries++; + } + cerr << "tested centroid: " << queries << endl; +} + +void test_area(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(range); + + auto gotA = 2*area(a, b, c); + auto gotB = 2*area(abs(a-b), abs(b-c), abs(c-a)); + auto expected = llround(gotA); + + if (float_error(gotA, expected) > 1e-6) cerr << "error: 1" << FAIL; + if (float_error(gotB, expected) > 1e-3) cerr << "error: 2" << FAIL; + queries++; + } + cerr << "tested area: " << queries << endl; +} + +void test_inCenter(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(range); + + pt center = inCenter(a, b, c); + + double da = distToLine(a, b, center); + double db = distToLine(b, c, center); + double dc = distToLine(c, a, center); + + double avg = (da + db + dc) / 3.0; + + if (float_error(da, avg) > 1e-6) cerr << "error: 1" << FAIL; + if (float_error(db, avg) > 1e-6) cerr << "error: 2" << FAIL; + if (float_error(dc, avg) > 1e-6) cerr << "error: 3" << FAIL; + queries++; + } + cerr << "tested inCenter: " << queries << endl; +} + +void test_circumCenter(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(range); + + pt center = circumCenter(a, b, c); + + double da = abs(center - a); + double db = abs(center - b); + double dc = abs(center - c); + + double avg = (da + db + dc) / 3.0; + + if (float_error(da, avg) > 1e-6) cerr << "error: 1" << FAIL; + if (float_error(db, avg) > 1e-6) cerr << "error: 2" << FAIL; + if (float_error(dc, avg) > 1e-6) cerr << "error: 3" << FAIL; + queries++; + } + cerr << "tested circumCenter: " << queries << endl; +} + +void test_insideOutCenter(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(range); + pt p = Random::integerPoint(range); + + pt center = circumCenter(a, b, c); + + double da = abs(center - a); + double db = abs(center - b); + double dc = abs(center - c); + double dp = abs(center - p); + + double avg = (da + db + dc) / 3.0; + + int expected = dp < avg ? 1 : -1; + if (float_error(dp, avg) < 1e-9) expected = 0; + + if (insideOutCenter(a, b, c, p) != expected) cerr << "error" << FAIL; + + queries++; + } + cerr << "tested insideOutCenter: " << queries << endl; +} + +void test_similar(ll range) { + int queries = 0; + for (int tries = 0; tries < 1'000'000; tries++) { + auto [a, b, c] = Random::triangle(sqrt(range)); + pt rot = Random::integerPoint(sqrt(range)); + pt add = Random::integerPoint(range); + + pt d = rot * a + add; + pt e = rot * b + add; + pt f = rot * c + add; + + if (!similar(a, b, c, d, e, f)) cerr << "error" << FAIL; + queries++; + } + cerr << "tested similar: " << queries << endl; +} + +int main() { + test_centroid(100); + test_centroid(1'000'000'000); + test_area(100); + test_area(1'000'000'000); + test_inCenter(100); + test_inCenter(1'000'000'000); + test_circumCenter(100); + test_circumCenter(1'000'000'000); + test_insideOutCenter(100); + test_insideOutCenter(1'000'000'000); + test_similar(100); + test_similar(1'000'000'000); +} diff --git a/test/graph/2sat.cpp b/test/graph/2sat.cpp new file mode 100644 index 0000000..fc3186e --- /dev/null +++ b/test/graph/2sat.cpp @@ -0,0 +1,133 @@ +#include "../util.h" +#include <graph/scc.cpp> +#define static vector<vector<int>> adj; static // hacky... +#include <graph/2sat.cpp> +#undef static +#undef adj + +struct RandomClause { + int a, b; + int type; + RandomClause(int n) : + a(Random::integer<int>(0, 2*n)), + b(Random::integer<int>(0, 2*n)), + type(Random::integer<int>(0, 8)) {} + + bool eval(vector<int>& sol) const { + bool ba = sol[a]; + bool bb = sol[b]; + if (type == 0) return !ba || bb; + if (type == 1) return ba == bb; + if (type == 2) return ba || bb; + if (type == 3) return ba != bb; + if (type == 4) return ba && bb; + if (type == 5) return !(ba && bb); + + if (type == 6) return ba; + if (type == 7) return !ba; + return false; + } + + void add(sat2& sat) const { + int va = a; + int vb = b; + if (type == 0) sat.addImpl(va, vb); + if (type == 1) sat.addEquiv(va, vb); + if (type == 2) sat.addOr(va, vb); + if (type == 3) sat.addXor(va, vb); + if (type == 4) sat.addAnd(va, vb); + if (type == 5) sat.addNand(va, vb); + + if (type == 6) sat.addTrue(va); + if (type == 7) sat.addFalse(va); + } + + friend ostream& operator<<(ostream& os, const RandomClause& c) { + if (c.a & 1) os << "-"; + os << (c.a >> 1); + if (c.type == 0) os << "=>"; + if (c.type == 1) os << "=="; + if (c.type == 2) os << "or"; + if (c.type == 3) os << "xor"; + if (c.type == 4) os << "and"; + if (c.type == 5) os << "nand"; + + if (c.type == 6) return os; + if (c.type == 7) return os << "==F"; + + if (c.b & 1) os << "-"; + os << (c.b >> 1); + return os; + } +}; + +bool naive(int n, const vector<RandomClause>& clauses) { + for (ll i = 0; i < (1ll << n); i++) { + vector<int> tmp(2*n); + for (ll j = 0; j < n; j++) { + tmp[(2*j) + ((i >> j) & 1)] = 1; + } + bool ok = true; + for (auto& c : clauses) ok &= c.eval(tmp); + if (ok) return true; + } + return false; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 12); + int m = Random::integer<int>(0, 30); + + vector<RandomClause> clauses; + for (int i = 0; i < m; i++) clauses.emplace_back(n); + + sat2 sat(n); + for (auto& c : clauses) c.add(sat); + adj = sat.adj; + + bool got = sat.solve(); + bool expected = naive(n, clauses); + + if (got) { + for (int i = 0; i < 2*n; i+=2) { + if (sat.sol[i] < 0) cerr << "error: invalid vars" << FAIL; + if (sat.sol[i+1] < 0) cerr << "error: invalid vars" << FAIL; + if (sat.sol[i] == sat.sol[i+1]) cerr << "error: inconsistent vars" << FAIL; + } + for (auto& c : clauses) { + if (!c.eval(sat.sol)) { + cerr << "error: inconsistent" << FAIL; + } + } + } + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 200'000; +constexpr int M = 500'000; +void performance_test() { + timer t; + vector<RandomClause> clauses; + for (int i = 0; i < M; i++) clauses.emplace_back(N); + t.start(); + sat2 sat(N); + for (auto& c : clauses) c.add(sat); + t.stop(); + adj = sat.adj; + t.start(); + hash_t hash = sat.solve(); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/LCA_sparse.cpp b/test/graph/LCA_sparse.cpp new file mode 100644 index 0000000..f6eb345 --- /dev/null +++ b/test/graph/LCA_sparse.cpp @@ -0,0 +1,63 @@ +#include "../util.h" +#include <datastructures/sparseTable.cpp> +#include <graph/LCA_sparse.cpp> +namespace expected { +#include <graph/hld.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(2, 30); + Graph<NoData> g(n); + g.tree(); + + vector<vector<int>> adj(n); + g.forEdges([&](int a, int b){ + adj[a].push_back(b); + adj[b].push_back(a); + }); + + LCA lca; + lca.init(adj, 0); + + expected::adj = adj; + expected::init(); + + for (int i = 0; i < n; i++) { + for (int j = 0; j <= i; j++) { + auto got = lca.getLCA(i, j); + auto expected = expected::get_lca(i, j); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.tree(); + vector<vector<int>> adj(N); + g.forEdges([&](int a, int b){ + adj[a].push_back(b); + adj[b].push_back(a); + }); + + hash_t hash = 0; + t.start(); + LCA lca; + lca.init(adj, 0); + for (int i = 1; i < N; i++) hash += lca.getLCA(i-1, i); + t.stop(); + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/TSP.cpp b/test/graph/TSP.cpp new file mode 100644 index 0000000..f9aab2e --- /dev/null +++ b/test/graph/TSP.cpp @@ -0,0 +1,67 @@ +#include "../util.h" +struct edge { + ll dist; + int to; +}; +constexpr ll INF = LL::INF; +#include <graph/TSP.cpp> + +vector<int> naive() { + int n = sz(dist); + vector<int> todo(n - 1); + iota(all(todo), 1); + vector<int> res; + ll best = LL::INF; + do { + int last = 0; + ll cur = 0; + for (int x : todo) { + cur += dist[last][x]; + last = x; + } + cur += dist[last][0]; + if (cur < best) { + best = cur; + res = todo; + res.insert(res.begin(), 0); + res.push_back(0); + } + } while (next_permutation(all(todo))); + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 100'000; i++) { + int n = Random::integer<int>(1, 9); + dist.assign(n, {}); + for (auto& v : dist) v = Random::integers<ll>(n, 0, 1000'000'000); + + auto expected = naive(); + auto got = TSP(); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 19; +void performance_test() { + timer t; + dist.assign(N, {}); + for (auto& v : dist) v = Random::integers<ll>(N, 0, 1000'000'000); + t.start(); + auto got = TSP(); + t.stop(); + + hash_t hash = 0; + for (int x : got) hash += x; + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/articulationPoints.bcc.cpp b/test/graph/articulationPoints.bcc.cpp new file mode 100644 index 0000000..15f5cf2 --- /dev/null +++ b/test/graph/articulationPoints.bcc.cpp @@ -0,0 +1,78 @@ +#include "../util.h" +struct edge { + ll from, to, id; +}; +#define Edge edge +#include <graph/articulationPoints.cpp> +#undef Edge +#include <datastructures/unionFind.cpp> + +vector<vector<int>> naiveBCC(int m) { + init(m); + + vector<int> seen(sz(adj), -1); + int run = 0; + for (int i = 0; i < sz(adj); i++) { + for (auto e : adj[i]) { + run++; + seen[i] = run; + vector<ll> todo = {e.to}; + seen[e.to] = run; + while (!todo.empty()) { + int c = todo.back(); + todo.pop_back(); + for (auto ee : adj[c]) { + if (seen[ee.to] == run) continue; + seen[ee.to] = run; + todo.push_back(ee.to); + } + } + for (auto ee : adj[i]) { + if (seen[ee.to] == run) unionSets(ee.id, e.id); + } + } + } + vector<vector<int>> res(m); + for (int i = 0; i < m; i++) { + res[findSet(i)].push_back(i); + } + for (auto& v : res) sort(all(v)); + res.erase(remove_if(all(res), [](const vector<int>& v){return sz(v) <= 1;}), res.end()); + sort(all(res)); + return res; +} + +void stress_test_bcc() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1))); + Graph<NoData, 0, 1> g(n); + g.erdosRenyi(m); + + adj.assign(n, {}); + int nextId = 0; + g.forEdges([&](int a, int b){ + adj[a].push_back({a, b, nextId}); + adj[b].push_back({b, a, nextId}); + nextId++; + }); + + auto expected = naiveBCC(nextId); + find(); + vector<vector<int>> got(sz(bcc)); + for (int i = 0; i < sz(bcc); i++) { + for (auto e : bcc[i]) got[i].push_back(e.id); + sort(all(got[i])); + } + sort(all(got)); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +int main() { + stress_test_bcc(); +} diff --git a/test/graph/articulationPoints.bridges.cpp b/test/graph/articulationPoints.bridges.cpp new file mode 100644 index 0000000..a1b89d2 --- /dev/null +++ b/test/graph/articulationPoints.bridges.cpp @@ -0,0 +1,64 @@ +#include "../util.h" +struct edge { + ll from, to, id; +}; +#define Edge edge +#include <graph/articulationPoints.cpp> +#undef Edge + +vector<bool> naiveBridges(const vector<pair<int, int>>& edges) { + vector<bool> res(sz(edges)); + + vector<int> seen(sz(adj), -1); + for (int i = 0; i < sz(edges); i++) { + auto [a, b] = edges[i]; + vector<int> todo = {a}; + seen[a] = i; + while (!todo.empty() && seen[b] != i) { + int c = todo.back(); + todo.pop_back(); + for (auto e : adj[c]) { + if (e.id == i) continue; + if (seen[e.to] == i) continue; + seen[e.to] = i; + todo.push_back(e.to); + } + } + res[i] = seen[b] != i; + } + return res; +} + +void stress_test_bridges() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1))); + Graph<NoData, 0, 1> g(n); + g.erdosRenyi(m); + + adj.assign(n, {}); + vector<pair<int, int>> edges; + g.forEdges([&](int a, int b){ + adj[a].push_back({a, b, sz(edges)}); + adj[b].push_back({b, a, sz(edges)}); + edges.emplace_back(a, b); + }); + + auto expected = naiveBridges(edges); + find(); + vector<bool> got(sz(edges)); + for (auto e : bridges) { + if (got[e.id]) cerr << "error: duclicate" << FAIL; + got[e.id] = true; + } + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +int main() { + stress_test_bridges(); +} diff --git a/test/graph/articulationPoints.cpp b/test/graph/articulationPoints.cpp new file mode 100644 index 0000000..2567a09 --- /dev/null +++ b/test/graph/articulationPoints.cpp @@ -0,0 +1,85 @@ +#include "../util.h" +struct edge { + ll from, to, id; +}; +#define Edge edge +#include <graph/articulationPoints.cpp> +#undef Edge + +vector<bool> naiveArt() { + vector<bool> res(sz(adj)); + + vector<int> seen(sz(adj), -1); + for (int i = 0; i < sz(adj); i++) { + if (adj[i].empty()) continue; + seen[i] = i; + vector<ll> todo = {adj[i][0].to}; + seen[todo[0]] = i; + while (!todo.empty()) { + int c = todo.back(); + todo.pop_back(); + for (auto e : adj[c]) { + if (seen[e.to] == i) continue; + seen[e.to] = i; + todo.push_back(e.to); + } + } + for (auto e : adj[i]) { + if (seen[e.to] != i) res[i] = true; + } + } + return res; +} + +void stress_test_art() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1))); + Graph<NoData, 0, 1> g(n); + g.erdosRenyi(m); + + adj.assign(n, {}); + int nextId = 0; + g.forEdges([&](int a, int b){ + adj[a].push_back({a, b, nextId}); + adj[b].push_back({b, a, nextId}); + nextId++; + }); + + auto expected = naiveArt(); + find(); + vector<bool> got = isArt; + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +constexpr int M = 2'000'000; +void performance_test() { + timer t; + Graph<NoData, 0, 1> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + int nextId = 0; + g.forEdges([&](int a, int b){ + adj[a].push_back({a, b, nextId}); + adj[b].push_back({b, a, nextId}); + nextId++; + }); + + t.start(); + find(); + t.stop(); + hash_t hash = sz(bridges) + sz(bcc); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test_art(); + performance_test(); +} diff --git a/test/graph/bellmannFord.cpp b/test/graph/bellmannFord.cpp new file mode 100644 index 0000000..92f1fef --- /dev/null +++ b/test/graph/bellmannFord.cpp @@ -0,0 +1,70 @@ +#include "../util.h" +constexpr ll INF = LL::INF; +struct edge { + int from, to; + ll cost; +}; +#include <graph/bellmannFord.cpp> +namespace floydWarshall { +#include <graph/floydWarshall.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + vector<ll> potential = Random::integers<ll>(n, 0, 1'000'000'000'000ll); + + vector<edge> edges; + floydWarshall::dist.assign(n, vector<ll>(n, INF)); + for (int i = 0; i < n; i++) floydWarshall::dist[i][i] = 0; + + Graph<NoData, true> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + ll w = Random::integer<ll>(1, 100'000'000'000ll); + w = potential[b] + w - potential[a]; + edges.push_back({a, b, w}); + floydWarshall::dist[a][b] = min(floydWarshall::dist[a][b], w); + }); + + floydWarshall::floydWarshall(); + for (int i = 0; i < n; i++) { + auto got = bellmannFord(n, edges, i); + auto expected = floydWarshall::dist[i]; + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 5'000; +constexpr int M = 20'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + vector<edge> edges; + g.forEdges([&](int a, int b){ + ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll); + ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll); + edges.push_back({a, b, w1}); + edges.push_back({b, a, w2}); + }); + + t.start(); + auto got = bellmannFord(N, edges, 0); + t.stop(); + hash_t hash = 0; + for (auto x : got) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/bitonicTSP.cpp b/test/graph/bitonicTSP.cpp new file mode 100644 index 0000000..7c448a2 --- /dev/null +++ b/test/graph/bitonicTSP.cpp @@ -0,0 +1,49 @@ +#include "../util.h" +namespace got { +#include <graph/bitonicTSP.cpp> +} +namespace expected { +#include <graph/bitonicTSPsimple.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + + vector<vector<double>> dist(n); + for (auto& v : dist) v = Random::reals<double>(n, 0, 1e18); + + got::dist = dist; + expected::dist = dist; + + auto got = got::bitonicTSP(); + auto expected = got::bitonicTSP(); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +//this is an easy graph... +constexpr int N = 5'000; +void performance_test() { + timer t; + got::dist = vector<vector<double>>(N); + for (auto& v : got::dist) v = Random::reals<double>(N, 0, 1e18); + + + t.start(); + auto got = got::bitonicTSP(); + t.stop(); + hash_t hash = 0; + for (auto x : got) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/bitonicTSPsimple.cpp b/test/graph/bitonicTSPsimple.cpp new file mode 100644 index 0000000..c79a0ef --- /dev/null +++ b/test/graph/bitonicTSPsimple.cpp @@ -0,0 +1,49 @@ +#include "../util.h" +namespace got { +#include <graph/bitonicTSPsimple.cpp> +} +namespace expected { +#include <graph/bitonicTSP.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + + vector<vector<double>> dist(n); + for (auto& v : dist) v = Random::reals<double>(n, 0, 1e18); + + got::dist = dist; + expected::dist = dist; + + auto got = got::bitonicTSP(); + auto expected = got::bitonicTSP(); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +//this is an easy graph... +constexpr int N = 2'000; +void performance_test() { + timer t; + got::dist = vector<vector<double>>(N); + for (auto& v : got::dist) v = Random::reals<double>(N, 0, 1e18); + + + t.start(); + auto got = got::bitonicTSP(); + t.stop(); + hash_t hash = 0; + for (auto x : got) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/blossom.cpp b/test/graph/blossom.cpp new file mode 100644 index 0000000..714b029 --- /dev/null +++ b/test/graph/blossom.cpp @@ -0,0 +1,76 @@ +#include "../util.h" +namespace tutte { +void gauss(int n, ll mod); +#include <graph/matching.cpp> +#include <math/shortModInv.cpp> +#include <math/lgsFp.cpp> +} +#include <graph/blossom.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 5'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1)); + + GM blossom(n); + srand(Random::rng()); + tutte::adj.assign(n, {}); + + Graph<NoData> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + tutte::adj[a].push_back(b); + tutte::adj[b].push_back(a); + + blossom.adj[a].push_back(b); + blossom.adj[b].push_back(a); + }); + + ll got = blossom.match(); + ll expected = tutte::max_matching(); + + vector<bool> seen(n); + ll got2 = 0; + for (int i = 0; i < n; i++) { + int j = blossom.pairs[i]; + if (j >= n) continue; + if (blossom.pairs[j] != i) cerr << "error: inconsitent" << FAIL; + if (j == i) cerr << "error: invalid" << FAIL; + if (j < i) continue; + if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL; + seen[i] = seen[j] = true; + got2++; + } + + if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +//this is an easy graph... +constexpr int N = 100'000; +constexpr int M = 500'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + GM blossom(N); + g.forEdges([&](int a, int b){ + blossom.adj[a].push_back(b); + blossom.adj[b].push_back(a); + }); + + t.start(); + hash_t hash = blossom.match(); + t.stop(); + if (t.time > 200) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/bronKerbosch.cpp b/test/graph/bronKerbosch.cpp new file mode 100644 index 0000000..1ccd493 --- /dev/null +++ b/test/graph/bronKerbosch.cpp @@ -0,0 +1,73 @@ +#include "../util.h" +#include <graph/bronKerbosch.cpp> + +vector<bits> naiveCliques; + +void naive(bits mask = {}, int l = 0) { + bool maximal = true; + for (ll i = 0; i < l; i++) { + if (mask[i]) continue; + if ((adj[i] & mask) == mask) maximal = false; + } + for (; l < sz(adj); l++) { + if ((adj[l] & mask) == mask) { + maximal = false; + mask[l] = 1; + naive(mask, l + 1); + mask[l] = 0; + } + } + if (maximal and mask.any()) naiveCliques.push_back(mask); +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(2, 15); + int m = Random::integer<int>(0, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + Graph<NoData> g(n); + g.erdosRenyi(m); + adj.assign(n, {}); + g.forEdges([&](int a, int b){ + addEdge(a, b); + }); + + bronKerbosch(); + naiveCliques.clear(); + naive(); + + sort(all(cliques), [](bits a, bits b){return a.to_ullong() < b.to_ullong();}); + sort(all(naiveCliques), [](bits a, bits b){return a.to_ullong() < b.to_ullong();}); + + if (cliques != naiveCliques) cerr << "got: " << sz(cliques) << ", expected: " << sz(naiveCliques) << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 55; +constexpr int M = N*(N-1) / 2 - 2*N; +void performance_test() { + timer t; + + Graph<NoData> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + g.forEdges([&](int a, int b){ + addEdge(a, b); + }); + + t.start(); + bronKerbosch(); + t.stop(); + + hash_t hash = sz(cliques); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/centroid.cpp b/test/graph/centroid.cpp new file mode 100644 index 0000000..41d9d0f --- /dev/null +++ b/test/graph/centroid.cpp @@ -0,0 +1,77 @@ +#include "../util.h" +vector<vector<int>> adj; +#include <graph/centroid.cpp> + +int subtreeSize(int c, int p) { + int res = 1; + for (int x : adj[c]) { + if (x == p) continue; + res += subtreeSize(x, c); + } + return res; +} + +vector<int> naive() { + vector<int> res; + for (int i = 0; i < sz(adj); i++) { + bool isCentroid = true; + for (int j : adj[i]) isCentroid &= 2*subtreeSize(j, i) <= sz(adj); + if (isCentroid) res.push_back(i); + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 50); + Graph<NoData> g(n); + g.tree(); + + adj.assign(n, {}); + g.forEdges([&](int a, int b){ + adj[a].push_back(b); + adj[b].push_back(a); + }); + + auto expected = naive(); + sort(all(expected)); + + for (int i = 0; i < n; i++) { + auto [a, b] = find_centroid(i); + vector<int> got; + if (a >= 0) got.push_back(a); + if (b >= 0) got.push_back(b); + sort(all(got)); + + if (got != expected) cerr << "error" << FAIL; + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 2'000'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.tree(); + + adj.assign(N, {}); + g.forEdges([&](int a, int b){ + adj[a].push_back(b); + adj[b].push_back(a); + }); + + t.start(); + auto [gotA, gotB] = find_centroid(); + t.stop(); + hash_t hash = gotA + gotB; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/cycleCounting.cpp b/test/graph/cycleCounting.cpp new file mode 100644 index 0000000..8e53aec --- /dev/null +++ b/test/graph/cycleCounting.cpp @@ -0,0 +1,79 @@ +#include "../util.h" +#include <datastructures/unionFind.cpp> +#include <graph/cycleCounting.cpp> + +int naive(const vector<pair<int, int>>& edges, int n) { + int res = 0; + for (int i = 1; i < (1ll << sz(edges)); i++) { + vector<int> deg(n); + init(n); + int cycles = 0; + for (int j = 0; j < sz(edges); j++) { + if (((i >> j) & 1) != 0) { + auto [a, b] = edges[j]; + deg[a]++; + deg[b]++; + if (findSet(a) != findSet(b)) { + unionSets(a, b); + } else { + cycles++; + } + } + } + bool ok = cycles == 1; + for (auto d : deg) ok &= d == 0 || d == 2; + if (ok) res++; + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 50'000; tries++) { + int n = Random::integer<int>(1, 8); + int m = Random::integer<int>(0, min<int>(15, n*(n-1) / 2 + 1)); + + Graph<NoData, 0, 1, 1> g(n); + g.erdosRenyi(m); + vector<pair<int, int>> edges; + cycles cyc(n); + g.forEdges([&](int a, int b){ + edges.emplace_back(a, b); + cyc.addEdge(a, b); + }); + + int expected = naive(edges, n); + int got = cyc.count(); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 100; +constexpr int M = 20; +void performance_test() { + timer t; + + Graph<NoData> g(N); + g.tree(); + g.erdosRenyi(M); + cycles cyc(N); + g.forEdges([&](int a, int b){ + cyc.addEdge(a, b); + }); + + t.start(); + hash_t hash = cyc.count(); + cerr << sz(cyc.base) << endl; + t.stop(); + + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/dijkstra.cpp b/test/graph/dijkstra.cpp new file mode 100644 index 0000000..c0cfb7e --- /dev/null +++ b/test/graph/dijkstra.cpp @@ -0,0 +1,64 @@ +#include "../util.h" +constexpr ll INF = LL::INF; +#include <graph/dijkstra.cpp> +struct edge { + int from, to; + ll cost; +}; +#include <graph/bellmannFord.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + vector<vector<path>> adj(n); + vector<edge> edges; + + Graph<NoData, true> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + ll w = Random::integer<ll>(1, 1'000'000'000'000ll); + adj[a].push_back({w, b}); + edges.push_back({a, b, w}); + }); + + for (int i = 0; i < n; i++) { + auto got = dijkstra(adj, i); + auto expected = bellmannFord(n, edges, i); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +constexpr int M = 3'000'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + vector<vector<path>> adj(N); + g.forEdges([&](int a, int b){ + ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll); + ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll); + adj[a].push_back({w1, b}); + adj[b].push_back({w2, a}); + }); + + t.start(); + auto got = dijkstra(adj, 0); + t.stop(); + hash_t hash = 0; + for (auto x : got) hash += x; + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/dinicScaling.cpp b/test/graph/dinicScaling.cpp new file mode 100644 index 0000000..967d6b1 --- /dev/null +++ b/test/graph/dinicScaling.cpp @@ -0,0 +1,61 @@ +#include "../util.h" +namespace dinic { +#include <graph/dinicScaling.cpp> +} + +namespace pushRelabel { +#include <graph/pushRelabel.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + dinic::adj.assign(n, {}); + pushRelabel::adj.assign(n, {}); + + Graph<NoData, true> g(n); + g.erdosRenyi(m); + g.forEdges([](int a, int b){ + ll w = Random::integer<ll>(1, 1'000'000'000'000ll); + dinic::addEdge(a, b, w); + pushRelabel::addEdge(a, b, w); + }); + + ll got = dinic::maxFlow(0, n - 1); + ll expected = pushRelabel::maxFlow(0, n - 1); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 50000; +constexpr int M = 200000; +void performance_test() { + using namespace dinic; + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + g.forEdges([](int a, int b){ + ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll); + ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll); + addEdge(a, b, w1); + addEdge(b, a, w2); + }); + + t.start(); + hash_t hash = maxFlow(0, N - 1); + t.stop(); + if (t.time > 2000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/euler.cpp b/test/graph/euler.cpp new file mode 100644 index 0000000..6666040 --- /dev/null +++ b/test/graph/euler.cpp @@ -0,0 +1,87 @@ +#include "../util.h" +struct Euler { + Euler(int n) : idx(n), validIdx(n) {} +#include <graph/euler.cpp> +}; + +Euler eulerGraph(int n, int m) { + Euler res(n); + + Graph<NoData> g(n); + g.tree(); + g.forEdges([&](int a, int b) { + res.addEdge(a, b); + }); + + for (int i = n-1; i < m; i++) { + int a = Random::integer<int>(0, n); + int b = Random::integer<int>(0, n); + res.addEdge(a, b); + } + int last = -1; + for (int i = 0; i < n; i++) { + if (sz(res.idx[i]) % 2 != 0) { + if (last >= 0) { + res.addEdge(last, i); + last = -1; + } else { + last = i; + } + } + } + if (last >= 0) cerr << "FAIL" << FAIL; + + return res; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(n-1, 200); + + auto g = eulerGraph(n, m); + + vector<vector<int>> expected(n); + for (int i = 0; i < n; i++) { + for (int j : g.idx[i]) { + expected[i].push_back(g.to[j]); + } + sort(all(expected[i])); + } + + g.euler(0); + vector<vector<int>> got(n); + if (g.cycle.front() != g.cycle.back()) cerr << "error: not cyclic" << FAIL; + for (int i = 1; i < sz(g.cycle); i++) { + int a = g.cycle[i-1]; + int b = g.cycle[i]; + got[a].push_back(b); + got[b].push_back(a); + } + for (auto& v : got) sort(all(v)); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 100'000; +constexpr int M = 1'000'000; +void performance_test() { + timer t; + auto g = eulerGraph(N, M); + t.start(); + g.euler(0); + t.stop(); + hash_t hash = 0; + for (int x : g.cycle) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/floydWarshall.cpp b/test/graph/floydWarshall.cpp new file mode 100644 index 0000000..a93a9ea --- /dev/null +++ b/test/graph/floydWarshall.cpp @@ -0,0 +1,90 @@ +#include "../util.h" +constexpr ll INF = LL::INF; +struct edge { + int from, to; + ll cost; +}; +#include <graph/bellmannFord.cpp> +namespace floydWarshall { +#include <graph/floydWarshall.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + vector<ll> potential = Random::integers<ll>(n, 0, 1'000'000'000'000ll); + + vector<edge> edges; + floydWarshall::dist.assign(n, vector<ll>(n, INF)); + for (int i = 0; i < n; i++) floydWarshall::dist[i][i] = 0; + + Graph<NoData, true> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + ll w = Random::integer<ll>(1, 100'000'000'000ll); + w = potential[b] + w - potential[a]; + edges.push_back({a, b, w}); + floydWarshall::dist[a][b] = min(floydWarshall::dist[a][b], w); + }); + + vector<vector<ll>> orig = floydWarshall::dist; + + floydWarshall::floydWarshall(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < 10; j++) { + int k = Random::integer<int>(0, n); + auto path = floydWarshall::getPath(i, k); + if (path.empty() != (floydWarshall::dist[i][k] == INF)) cerr << "error: reconstruction" << FAIL; + if (path.empty()) continue; + if (path.front() != i) cerr << "error: start" << FAIL; + if (path.back() != k) cerr << "error: end" << FAIL; + for (int l = 1; l < sz(path); l++) { + if (floydWarshall::dist[i][path[l-1]] + + orig[path[l-1]][path[l]] + + floydWarshall::dist[path[l]][k] != + floydWarshall::dist[i][k]) cerr << "error: edge" << FAIL; + } + } + } + + for (int i = 0; i < n; i++) { + auto got = floydWarshall::dist[i]; + auto expected = bellmannFord(n, edges, i); + + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500; +constexpr int M = 20'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + floydWarshall::dist.assign(N, vector<ll>(N, INF)); + for (int i = 0; i < N; i++) floydWarshall::dist[i][i] = 0; + g.forEdges([&](int a, int b){ + ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll); + ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll); + floydWarshall::dist[a][b] = w1; + floydWarshall::dist[b][a] = w2; + }); + + t.start(); + floydWarshall::floydWarshall(); + t.stop(); + hash_t hash = 0; + for (auto x : floydWarshall::dist[42]) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/havelHakimi.cpp b/test/graph/havelHakimi.cpp new file mode 100644 index 0000000..71476ec --- /dev/null +++ b/test/graph/havelHakimi.cpp @@ -0,0 +1,65 @@ +#include "../util.h" +#include <graph/havelHakimi.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 200'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, n*(n-1) / 2 + 1); + Graph g(n); + g.erdosRenyi(m); + + vector<int> expected(n); + for (int i = 0; i < n; i++) expected[i] = g.deg(i); + + auto res = havelHakimi(expected); + if (sz(res) != n) cerr << "error: wrong number of nodes" << FAIL; + vector<vector<int>> rev(n); + vector<int> got(n); + for (int i = 0; i < n; i++) { + got[i] = sz(res[i]); + for (int j : res[i]) { + if (j < 0 || j >= n) cerr << "error: invalid edge" << FAIL; + rev[j].push_back(i); + } + } + + for (int i = 0; i < n; i++) { + sort(all(res[i])); + sort(all(rev[i])); + if (res[i] != rev[i]) cerr << "error: graph is directed" << FAIL; + for (int j : res[i]) if (j == i) cerr << "error: graph has loop" << FAIL; + for (int j = 1; j < sz(res[i]); j++) { + if (res[i][j] == res[i][j-1]) cerr << "error: multiedge" << FAIL; + } + } + + if (expected != got) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 200'000; +constexpr int M = 1'000'000; +void performance_test() { + timer t; + Graph g(N); + g.erdosRenyi(M); + + vector<int> expected(N); + for (int i = 0; i < N; i++) expected[i] = g.deg(i); + + t.start(); + auto res = havelHakimi(expected); + t.stop(); + hash_t hash = 0; + for (auto& v : res) hash += sz(v); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/hopcroftKarp.cpp b/test/graph/hopcroftKarp.cpp new file mode 100644 index 0000000..05599dd --- /dev/null +++ b/test/graph/hopcroftKarp.cpp @@ -0,0 +1,74 @@ +#include "../util.h" +namespace kuhn { +#include <graph/maxCarBiMatch.cpp> +} +namespace hk { +#include <graph/hopcroftKarp.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 50'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1)); + + kuhn::adj.assign(2*n, {}); + hk::adj.assign(2*n, {}); + + Graph<NoData> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + kuhn::adj[a].push_back(n+b); + kuhn::adj[b+n].push_back(a); + + hk::adj[a].push_back(n+b); + hk::adj[b+n].push_back(a); + }); + + ll got = hk::hopcroft_karp(n); + ll expected = kuhn::kuhn(n); + + vector<bool> seen(2*n); + ll got2 = 0; + for (int i = 0; i < n; i++) { + int j = hk::pairs[i]; + if (j < 0) continue; + if (hk::pairs[j] != i) cerr << "error: inconsitent" << FAIL; + if (j == i) cerr << "error: invalid" << FAIL; + if (j < i) continue; + if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL; + seen[i] = seen[j] = true; + got2++; + } + + if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +//this is an easy graph... +constexpr int N = 100'000; +constexpr int M = 500'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + hk::adj.assign(2*N, {}); + g.forEdges([&](int a, int b){ + hk::adj[a].push_back(N+b); + hk::adj[b+N].push_back(a); + }); + + t.start(); + hash_t hash = hk::hopcroft_karp(N); + t.stop(); + if (t.time > 300) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/kruskal.cpp b/test/graph/kruskal.cpp new file mode 100644 index 0000000..f6245b9 --- /dev/null +++ b/test/graph/kruskal.cpp @@ -0,0 +1,91 @@ +#include "../util.h" +#include <datastructures/unionFind.cpp> + +struct edge { + int from, to; + ll cost; + bool operator<(const edge& o) const { + return cost > o.cost; + } +}; +ll kruskal(vector<edge>& edges, int n) { + init(n); + #define Edge edge + #include <graph/kruskal.cpp> + #undef Edge + return cost; +} + +ll prim(vector<edge>& edges, int n) { + vector<vector<pair<ll, int>>> adj(n); + for (auto [a, b, d] : edges) { + adj[a].emplace_back(d, b); + adj[b].emplace_back(d, a); + } + priority_queue<pair<ll, int>> todo; + vector<bool> seen(n); + ll res = 0; + for (ll i = 0; i < n; i++) { + if (seen[i]) continue; + todo.push({0, i}); + while (!todo.empty()) { + auto [d, c] = todo.top(); + todo.pop(); + if (seen[c]) continue; + seen[c] = true; + res += d; + for (auto e : adj[c]) { + todo.push(e); + } + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(0, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + + Graph<NoData> g(n); + g.erdosRenyi(m); + vector<edge> edges; + g.forEdges([&](int a, int b){ + ll w = Random::integer<ll>(-1'000'000'000ll, 1'000'000'000ll); + edges.push_back({a, b, w}); + }); + + ll got = kruskal(edges, n); + ll expected = prim(edges, n); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +constexpr int M = 3'000'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + vector<edge> edges; + g.forEdges([&](int a, int b){ + ll w = Random::integer<ll>(-1'000'000'000ll, 1'000'000'000ll); + edges.push_back({a, b, w}); + }); + + t.start(); + hash_t hash = kruskal(edges, N); + t.stop(); + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/matching.cpp b/test/graph/matching.cpp new file mode 100644 index 0000000..b8fbc6c --- /dev/null +++ b/test/graph/matching.cpp @@ -0,0 +1,62 @@ +#include "../util.h" +namespace tutte { +void gauss(int n, ll mod); +#include <graph/matching.cpp> +#include <math/shortModInv.cpp> +#include <math/lgsFp.cpp> +} +#include <graph/blossom.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 5'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1)); + + GM blossom(n); + srand(Random::rng()); + tutte::adj.assign(n, {}); + + Graph<NoData> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + tutte::adj[a].push_back(b); + tutte::adj[b].push_back(a); + + blossom.adj[a].push_back(b); + blossom.adj[b].push_back(a); + }); + + ll got = tutte::max_matching(); + ll expected = blossom.match(); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 125; +constexpr int M = 5'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + srand(Random::rng()); + tutte::adj.assign(N, {}); + g.forEdges([&](int a, int b){ + tutte::adj[a].push_back(b); + tutte::adj[b].push_back(a); + }); + + t.start(); + hash_t hash = tutte::max_matching(); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/maxCarBiMatch.cpp b/test/graph/maxCarBiMatch.cpp new file mode 100644 index 0000000..6d7fad0 --- /dev/null +++ b/test/graph/maxCarBiMatch.cpp @@ -0,0 +1,74 @@ +#include "../util.h" +namespace kuhn { +#include <graph/maxCarBiMatch.cpp> +} +namespace hk { +#include <graph/hopcroftKarp.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 50'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1)); + + kuhn::adj.assign(2*n, {}); + hk::adj.assign(2*n, {}); + + Graph<NoData> g(n); + g.erdosRenyi(m); + g.forEdges([&](int a, int b){ + kuhn::adj[a].push_back(n+b); + kuhn::adj[b+n].push_back(a); + + hk::adj[a].push_back(n+b); + hk::adj[b+n].push_back(a); + }); + + ll got = kuhn::kuhn(n); + ll expected = hk::hopcroft_karp(n); + + vector<bool> seen(2*n); + ll got2 = 0; + for (int i = 0; i < n; i++) { + int j = kuhn::pairs[i]; + if (j < 0) continue; + if (kuhn::pairs[j] != i) cerr << "error: inconsitent" << FAIL; + if (j == i) cerr << "error: invalid" << FAIL; + if (j < i) continue; + if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL; + seen[i] = seen[j] = true; + got2++; + } + + if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL; + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +//this is an easy graph... +constexpr int N = 10'000; +constexpr int M = 100'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + kuhn::adj.assign(2*N, {}); + g.forEdges([&](int a, int b){ + kuhn::adj[a].push_back(N+b); + kuhn::adj[b+N].push_back(a); + }); + + t.start(); + hash_t hash = kuhn::kuhn(N); + t.stop(); + if (t.time > 200) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/maxWeightBipartiteMatching.cpp b/test/graph/maxWeightBipartiteMatching.cpp new file mode 100644 index 0000000..d245405 --- /dev/null +++ b/test/graph/maxWeightBipartiteMatching.cpp @@ -0,0 +1,59 @@ +#include "../util.h" +#pragma GCC diagnostic ignored "-Wshadow" +namespace matching { + constexpr int N_LEFT = 1000; + constexpr int N_RIGHT = 1000; + constexpr double INF = LD::INF; + #include <graph/maxWeightBipartiteMatching.cpp> +} +namespace mcmf { + #include <graph/minCostMaxFlow.cpp> +} + + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + auto [l, r] = Random::pair<int>(1, 30); + mcmf::MinCostFlow mcmf(l+r+2, 0, 1); + + for (int i = 0; i < l; i++) mcmf.addEdge(0, 2 + i, 1, 0); + for (int i = 0; i < r; i++) mcmf.addEdge(2 + l + i, 1, 1, 0); + for (int i = 0; i < l; i++) { + for (int j = 0; j < r; j++) { + matching::costs[i][j] = Random::integer<int>(-100, 100); + mcmf.addEdge(2 + i, 2 + l + j, 1, -matching::costs[i][j]); + } + } + + double got = matching::match(l, r); + mcmf.mincostflow(); + ll expected = -mcmf.mincost; + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += l + r; + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test() { + using namespace matching; + timer t; + + for (int i = 0; i < N_LEFT; i++) { + for (int j = 0; j < N_RIGHT; j++) { + costs[i][j] = Random::integer<int>(-100, 100); + } + } + + t.start(); + hash_t hash = match(N_LEFT, N_RIGHT); + t.stop(); + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/minCostMaxFlow.cpp b/test/graph/minCostMaxFlow.cpp new file mode 100644 index 0000000..8c92aa7 --- /dev/null +++ b/test/graph/minCostMaxFlow.cpp @@ -0,0 +1,68 @@ +#include "../util.h" +#pragma GCC diagnostic ignored "-Wshadow" +namespace matching { + constexpr int N_LEFT = 1000; + constexpr int N_RIGHT = 1000; + constexpr double INF = LD::INF; + #include <graph/maxWeightBipartiteMatching.cpp> +} +namespace mcmf { + #include <graph/minCostMaxFlow.cpp> +} + + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + auto [l, r] = Random::pair<int>(1, 30); + mcmf::MinCostFlow mcmf(l+r+2, 0, 1); + + for (int i = 0; i < l; i++) mcmf.addEdge(0, 2 + i, 1, 0); + for (int i = 0; i < r; i++) mcmf.addEdge(2 + l + i, 1, 1, 0); + for (int i = 0; i < l; i++) { + for (int j = 0; j < r; j++) { + matching::costs[i][j] = Random::integer<int>(-100, 100); + mcmf.addEdge(2 + i, 2 + l + j, 1, -matching::costs[i][j]); + } + } + + mcmf.mincostflow(); + ll got = -mcmf.mincost; + double expected = matching::match(l, r); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += l + r; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000; +constexpr int M = 10'000; +void performance_test() { + using namespace mcmf; + timer t; + + Graph<NoData> g(N); + g.erdosRenyi(M); + MinCostFlow mcmf(N, 0, 1); + vector<ll> potential = Random::integers<ll>(N, 0, 1'000'000ll); + g.forEdges([&](int a, int b){ + ll c = Random::integer<ll>(1, 1000'000); + ll cost = Random::integer<ll>(0, 1000'000); + mcmf.addEdge(a, b, c, potential[b] + cost - potential[a]); + mcmf.addEdge(b, a, c, potential[a] + cost - potential[b]); + }); + + t.start(); + mcmf.mincostflow(); + t.stop(); + + hash_t hash = mcmf.mincost; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/pushRelabel.cpp b/test/graph/pushRelabel.cpp new file mode 100644 index 0000000..ac3b079 --- /dev/null +++ b/test/graph/pushRelabel.cpp @@ -0,0 +1,61 @@ +#include "../util.h" +namespace dinic { +#include <graph/dinicScaling.cpp> +} + +namespace pushRelabel { +#include <graph/pushRelabel.cpp> +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + dinic::adj.assign(n, {}); + pushRelabel::adj.assign(n, {}); + + Graph<NoData, true> g(n); + g.erdosRenyi(m); + g.forEdges([](int a, int b){ + ll w = Random::integer<ll>(1, 1'000'000'000'000ll); + dinic::addEdge(a, b, w); + pushRelabel::addEdge(a, b, w); + }); + + ll got = pushRelabel::maxFlow(0, n - 1); + ll expected = dinic::maxFlow(0, n - 1); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 50000; +constexpr int M = 200000; +void performance_test() { + using namespace pushRelabel; + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + g.forEdges([](int a, int b){ + ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll); + ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll); + addEdge(a, b, w1); + addEdge(b, a, w2); + }); + + t.start(); + hash_t hash = maxFlow(0, N - 1); + t.stop(); + if (t.time > 300) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/scc.cpp b/test/graph/scc.cpp new file mode 100644 index 0000000..123050f --- /dev/null +++ b/test/graph/scc.cpp @@ -0,0 +1,92 @@ +#include "../util.h" +#include <graph/scc.cpp> +#include <datastructures/unionFind.cpp> + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 30); + int m = Random::integer<int>(0, max<int>(1, min<int>(100, n*(n-1) / 2 + 1))); + Graph<NoData, true> g(n); + g.erdosRenyi(m); + + adj.assign(n, {}); + g.forEdges([](int a, int b){ + adj[a].push_back(b); + }); + scc(); + + vector<bool> tmp(n); + for (int i = 0; i < sz(sccs); i++) { + for (int x : sccs[i]) { + if (tmp[x]) cerr << "error: duclicate" << FAIL; + if (idx[x] != i) cerr << "error: inconsistent" << FAIL; + tmp[x] = true; + } + } + for (int i = 0; i < n; i++) { + if (!tmp[i]) cerr << "error: missing" << FAIL; + } + + init(n); + vector<ll> seen(n); + int tmpCounter = 0; + auto reach = [&](int a, int b) { + tmpCounter++; + seen[a] = tmpCounter; + vector<int> todo = {a}; + while (seen[b] != tmpCounter && !todo.empty()) { + a = todo.back(); + todo.pop_back(); + g.forOut(a, [&](int /**/, int x){ + if (seen[x] != tmpCounter) { + seen[x] = tmpCounter; + todo.push_back(x); + } + }); + } + return seen[b] == tmpCounter; + }; + for (int a = 0; a < n; a++) { + for (int b = 0; b < a; b++) { + if (findSet(a) == findSet(b)) continue; + if (reach(a, b) && reach(b, a)) unionSets(a, b); + } + } + + for (int a = 0; a < n; a++) { + for (int b = 0; b <= a; b++) { + bool got = idx[a] == idx[b]; + bool expected = findSet(a) == findSet(b); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + } + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +constexpr int M = 2'000'000; +void performance_test() { + timer t; + Graph<NoData, true> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + g.forEdges([](int a, int b){ + adj[a].push_back(b); + }); + + t.start(); + scc(); + t.stop(); + hash_t hash = 0; + for (int x : idx) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/stoerWagner.cpp b/test/graph/stoerWagner.cpp new file mode 100644 index 0000000..2003f09 --- /dev/null +++ b/test/graph/stoerWagner.cpp @@ -0,0 +1,81 @@ +#include "../util.h" +constexpr ll INF = LL::INF; + +namespace stoerWagner { +#include <graph/stoerWagner.cpp> + void addEdge(int u, int v, ll c) { + adj[u].push_back({u, v, c}); + adj[v].push_back({v, u, c}); + } +} + +namespace pushRelabel { +#include <graph/pushRelabel.cpp> + ll minCut() { + ll res = INF; + for (int i = 0; i < sz(adj); i++) { + for (int j = 0; j < i; j++) { + if (i == j) continue; + res = min(res, maxFlow(i, j)); + for (auto& v : adj) { + for (auto& e : v) { + e.f = 0; + } + } + } + } + return res; + } +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 5'000; tries++) { + int n = Random::integer<int>(2, 30); + int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1))); + + stoerWagner::adj.assign(n, {}); + pushRelabel::adj.assign(n, {}); + + Graph<NoData> g(n); + g.erdosRenyi(m); + g.forEdges([](int a, int b){ + ll w = Random::integer<ll>(1, 1'000'000'000'000ll); + stoerWagner::addEdge(a, b, w); + pushRelabel::addEdge(a, b, w); + pushRelabel::addEdge(b, a, w); + }); + + ll got = stoerWagner::stoer_wagner(); + ll expected = pushRelabel::minCut(); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 200; +constexpr int M = 10000; +void performance_test() { + using namespace stoerWagner; + timer t; + Graph<NoData> g(N); + g.erdosRenyi(M); + adj.assign(N, {}); + g.forEdges([](int a, int b){ + ll w = Random::integer<ll>(1, 1'000'000'000'000ll); + addEdge(a, b, w); + }); + + t.start(); + hash_t hash = stoer_wagner(); + t.stop(); + if (t.time > 2000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/graph/treeIsomorphism.cpp b/test/graph/treeIsomorphism.cpp new file mode 100644 index 0000000..97f4df4 --- /dev/null +++ b/test/graph/treeIsomorphism.cpp @@ -0,0 +1,126 @@ +#include "../util.h" +struct tree { + tree(int n) : adj(n) {} + #include <graph/treeIsomorphism.cpp> + #include <graph/centroid.cpp> + + pair<int, int> treeLabel() { + auto [a, b] = find_centroid(0); + if (a >= 0) a = treeLabel(a); + if (b >= 0) b = treeLabel(b); + if (a > b) swap(a, b); + return {a, b}; + } +}; + +void stress_test_eq() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 50); + Graph<NoData> g(n); + g.tree(); + + tree t(n); + + g.forEdges([&](int a, int b){ + t.adj[a].push_back(b); + t.adj[b].push_back(a); + }); + auto [gotA, gotB] = t.treeLabel(); + + g.permutate(); + t.adj.assign(n, {}); + g.forEdges([&](int a, int b){ + t.adj[a].push_back(b); + t.adj[b].push_back(a); + }); + auto [expectedA, expectedB] = t.treeLabel(); + + if (gotA != expectedA) cerr << "got: " << gotA << ", expected: " << expectedA << FAIL; + if (gotB != expectedB) cerr << "got: " << gotB << ", expected: " << expectedB << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +void test_tiny() { + vector<int> expected = {1,1,1,1,2,3,6,11,23}; //#A000055 + for (int i = 1; i < sz(expected); i++) { + set<pair<int, int>> got; + tree t(i); + + int labeled = 1; + for (int j = 3; j < i; j++) labeled *= i; + for (int j = 0; j < 10 * labeled; j++) { + Graph<NoData> g(i); + g.tree(); + + t.adj.assign(i, {}); + g.forEdges([&](int a, int b){ + t.adj[a].push_back(b); + t.adj[b].push_back(a); + }); + + got.insert(t.treeLabel()); + } + if (sz(got) != expected[i]) cerr << i << ", got: " << sz(got) << ", expected: " << expected[i] << FAIL; + } + cerr << "tested tiny: " << sz(expected) << endl; +} + +void stress_test_neq() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(20, 50); + Graph<NoData> g(n); + g.tree(); + + tree t(n); + + g.forEdges([&](int a, int b){ + t.adj[a].push_back(b); + t.adj[b].push_back(a); + }); + auto [gotA, gotB] = t.treeLabel(); + + g.clear().tree(); + t.adj.assign(n, {}); + g.forEdges([&](int a, int b){ + t.adj[a].push_back(b); + t.adj[b].push_back(a); + }); + auto [expectedA, expectedB] = t.treeLabel(); + + if (gotA == expectedA && gotA >= 0) cerr << "error: " << n << ", " << tries << FAIL; + if (gotB == expectedB) cerr << "error: " << n << ", " << tries << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + Graph<NoData> g(N); + g.tree(); + + tree tt(N); + g.forEdges([&](int a, int b){ + tt.adj[a].push_back(b); + tt.adj[b].push_back(a); + }); + + t.start(); + auto [gotA, gotB] = tt.treeLabel(); + t.stop(); + hash_t hash = gotA + gotB; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test_eq(); + test_tiny(); + stress_test_neq(); + performance_test(); +} diff --git a/test/math/berlekampMassey.cpp b/test/math/berlekampMassey.cpp new file mode 100644 index 0000000..58fd143 --- /dev/null +++ b/test/math/berlekampMassey.cpp @@ -0,0 +1,68 @@ +#include "../util.h" +#include <math/modPowIterativ.cpp> +#include <math/berlekampMassey.cpp> + +struct RandomRecurence { + vector<ll> f, c, cache; + RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {} + RandomRecurence(const vector<ll>& f_, const vector<ll>& c_) : c(c_), cache(f_) { + if (cache.size() < c.size()) cerr << "wrong size" << FAIL; + cache.resize(c.size()); + f = cache; + } + + ll operator()(ll k){ + while (sz(cache) <= k) { + ll cur = 0; + for (ll i = 0; i < sz(c); i++) { + cur += (c[i] * cache[sz(cache) - i - 1]) % mod; + } + cur %= mod; + cache.push_back(cur); + } + return cache[k]; + } +}; + +void stress_test() { + int queries = 0; + for (int i = 0; i < 50'000; i++) { + int n = Random::integer<int>(1, 10); + RandomRecurence expected(n); + + ll k = Random::integer<ll>(2*n, 100); + vector<ll> s(k); + for (ll j = 0; j < k; j++) s[j] = expected(j); + + auto res = BerlekampMassey(s); + RandomRecurence got(s, res); + + for (ll j = 0; j < 3*k; j++) { + if (got(j) != expected(j)) cerr << "got: " << got(j) << ", expected: " << expected(j) << FAIL; + } + + queries += k; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 5'000; +void performance_test() { + timer t; + RandomRecurence f(N); + f(2*N); + t.start(); + auto res = BerlekampMassey(f.cache); + t.stop(); + hash_t hash = 0; + for (ll x : res) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/bigint.cpp b/test/math/bigint.cpp new file mode 100644 index 0000000..3fc4ac1 --- /dev/null +++ b/test/math/bigint.cpp @@ -0,0 +1,122 @@ +#include "../util.h" +#include <math/bigint.cpp> + +template<ll MOD> +struct modInt { + ll value = 0; + modInt() {} + modInt(const bigint& x) { + stringstream a; + a << x; + string b = a.str(); + for (ll i = b[0] == '-' ? 1 : 0; i < sz(b); i++) { + value *= 10; + value += b[i] - '0'; + value %= MOD; + } + if (b[0] == '-') value = (MOD - value) % MOD; + } + + modInt(ll x) : value(((x % MOD) + MOD) % MOD) {} + + modInt operator+(modInt o) const {return value + o.value;} + modInt operator-(modInt o) const {return value - o.value;} + modInt operator*(modInt o) const {return value * o.value;} + + modInt& operator+=(modInt o) {return *this = *this + o;} + modInt& operator-=(modInt o) {return *this = *this - o;} + modInt& operator*=(modInt o) {return *this = *this * o;} + + ll& operator*() {return value;} + bool operator==(const modInt& o) const {return value == o.value;} + bool operator!=(const modInt& o) const {return value != o.value;} +}; + +constexpr ll MOD = 1'394'633'899; +constexpr ll POOL = 8; + +void stress_test() { + int queries = 0; + for (int tries = 0; tries < 1000; tries++) { + vector<modInt<MOD>> expectedPool(POOL); + vector<bigint> gotPool(POOL); + for (int i = 0; i < POOL; i++) { + ll x = Random::integer<ll>(-1'000'000'000'000'000'000ll, 1'000'000'000'000'000'000ll); + expectedPool[i] = x; + gotPool[i] = x; + if (expectedPool[i] != modInt<MOD>(gotPool[i])) cerr << "error: 0" << FAIL; + } + for (int i = 0; i < 200; i++) { + int a = Random::integer<int>(0, POOL); + int b = Random::integer<int>(0, POOL); + int o = Random::integer<int>(0, 3); + + if (Random::integer<int>(0, 2) == 0) {//x= + auto tmpExpected = expectedPool[a]; + auto tmpGot = gotPool[a]; + + if (o == 0) { + tmpExpected += expectedPool[b]; + tmpGot += gotPool[b]; + } + if (o == 1) { + tmpExpected -= expectedPool[b]; + tmpGot -= gotPool[b]; + } + if (o == 2) { + tmpExpected -= expectedPool[b]; + tmpGot -= gotPool[b]; + } + + if (tmpExpected != modInt<MOD>(tmpGot)) { + cerr << gotPool[a]; + if (o == 0) cerr << "+"; + if (o == 1) cerr << "-"; + if (o == 2) cerr << "*"; + cerr << gotPool[b] << "=" << tmpGot << endl; + cerr << "error: 1" << FAIL; + } + + expectedPool[b] = tmpExpected; + gotPool[b] = tmpGot; + } else {//x + int c = Random::integer<int>(0, POOL); + + modInt<MOD> tmpExpected; + bigint tmpGot; + + if (o == 0) { + tmpExpected = expectedPool[a] + expectedPool[b]; + tmpGot = gotPool[a] + gotPool[b]; + } + if (o == 1) { + tmpExpected = expectedPool[a] - expectedPool[b]; + tmpGot = gotPool[a] - gotPool[b]; + } + if (o == 2) { + tmpExpected = expectedPool[a] * expectedPool[b]; + tmpGot = gotPool[a] * gotPool[b]; + } + + if (tmpExpected != modInt<MOD>(tmpGot)) { + cerr << gotPool[a]; + if (o == 0) cerr << "+"; + if (o == 1) cerr << "-"; + if (o == 2) cerr << "*"; + cerr << gotPool[b] << "=" << tmpGot << endl; + cerr << "error: 2" << FAIL; + } + + expectedPool[c] = tmpExpected; + gotPool[c] = tmpGot; + } + queries++; + } + } + cerr << "tested random queries: " << queries << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/binomial0.cpp b/test/math/binomial0.cpp new file mode 100644 index 0000000..00c04d4 --- /dev/null +++ b/test/math/binomial0.cpp @@ -0,0 +1,31 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> +#include <math/multInv.cpp> +constexpr ll mod = 1'394'633'899; +#include <math/binomial0.cpp> + + +void stress_test() { + vector<ll> last = {1}; + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + for (ll j = 0; j <= i; j++) { + ll got = calc_binom(i, j); + ll expected = last[j]; + if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL; + } + queries += sz(last); + + last.push_back(1); + for (ll j = i; j > 0; j--) { + last[j] = (last[j] + last[j - 1]) % mod; + } + } + cerr << "tested queries: " << queries << endl; +} + +int main() { + precalc(); + stress_test(); +} + diff --git a/test/math/binomial1.cpp b/test/math/binomial1.cpp new file mode 100644 index 0000000..f6fe20b --- /dev/null +++ b/test/math/binomial1.cpp @@ -0,0 +1,27 @@ +#include "../util.h" +#include <math/binomial1.cpp> + + +void stress_test() { + vector<ll> last = {1}; + ll queries = 0; + for (ll i = 0; i <= 61; i++) { + for (ll j = 0; j <= i; j++) { + ll got = calc_binom(i, j); + ll expected = last[j]; + if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL; + } + queries += sz(last); + + last.push_back(1); + for (ll j = i; j > 0; j--) { + last[j] = last[j] + last[j - 1]; + } + } + cerr << "tested queries: " << queries << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/binomial2.cpp b/test/math/binomial2.cpp new file mode 100644 index 0000000..b55c8af --- /dev/null +++ b/test/math/binomial2.cpp @@ -0,0 +1,29 @@ +#include "../util.h" +#include <math/primeSieve.cpp> +#include <math/binomial2.cpp> + + +void stress_test() { + vector<ll> last = {1}; + ll queries = 0; + for (ll i = 0; i <= 1000; i++) { + for (ll j = 0; j <= i; j++) { + ll got = calc_binom(i, j); + ll expected = last[j]; + if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL; + } + queries += sz(last); + + last.push_back(1); + for (ll j = i; j > 0; j--) { + last[j] = (last[j] + last[j - 1]) % mod; + } + } + cerr << "tested queries: " << queries << endl; +} + +int main() { + primeSieve(); + stress_test(); +} + diff --git a/test/math/binomial3.cpp b/test/math/binomial3.cpp new file mode 100644 index 0000000..4a99689 --- /dev/null +++ b/test/math/binomial3.cpp @@ -0,0 +1,31 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> +#include <math/multInv.cpp> +#include <math/binomial3.cpp> + + +constexpr ll mod = 503; + +void stress_test() { + vector<ll> last = {1}; + ll queries = 0; + for (ll i = 0; i < mod; i++) { + for (ll j = 0; j <= i; j++) { + ll got = calc_binom(i, j, mod); + ll expected = last[j]; + if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL; + } + queries += sz(last); + + last.push_back(1); + for (ll j = i; j > 0; j--) { + last[j] = (last[j] + last[j - 1]) % mod; + } + } + cerr << "tested queries: " << queries << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/chineseRemainder.cpp b/test/math/chineseRemainder.cpp new file mode 100644 index 0000000..26e71de --- /dev/null +++ b/test/math/chineseRemainder.cpp @@ -0,0 +1,47 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> +#include <math/chineseRemainder.cpp> + +struct NAIVE { + vector<pair<ll, ll>> added; + void add(ll a, ll m) { + added.emplace_back(a, m); + } + ll sol() const { + ll n = 1; + for (auto [_, x] : added) n = lcm(n, x); + for (ll i = 0; i < n; i++) { + bool ok = true; + for (auto [a, m] : added) { + ok &= (i % m) == a; + } + if (ok) return i; + } + return -1; + } +}; + +void stress_test() { + ll queries = 0; + ll withSol = 0; + for (ll i = 0; i < 100'000; i++) { + CRT crt; + NAIVE naive; + for (ll j = 0; j < 3; j++) { + int m = Random::integer<int>(1, 50); + int a = Random::integer<int>(0, m); + crt.add(a, m); + naive.add(a, m); + } + if (crt.hasSol != (naive.sol() >= 0)) cerr << "error" << FAIL; + if (crt.hasSol && crt.sol != naive.sol()) cerr << "got: " << (ll)crt.sol << ", expected: " << naive.sol() << FAIL; + queries += crt.M; + withSol += crt.hasSol; + } + cerr << "tested queries: " << queries << "(" << withSol << ")" << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/cycleDetection.cpp b/test/math/cycleDetection.cpp new file mode 100644 index 0000000..bf57aed --- /dev/null +++ b/test/math/cycleDetection.cpp @@ -0,0 +1,47 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> +#include <math/cycleDetection.cpp> + +pair<ll, ll> naive(ll x0, function<ll(ll)> f) { + map<ll, ll> seen; + ll d = 0; + while (seen.find(x0) == seen.end()) { + seen[x0] = d; + d++; + x0 = f(x0); + } + return {seen[x0], d - seen[x0]}; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 1000'000; i++) { + int m = Random::integer<int>(1, 100); + int c = Random::integer<int>(0, m); + auto f = [&](ll x){return (x*x + c) % m;}; + int x0 = Random::integer<int>(0, m); + auto got = cycleDetection(x0, f); + auto expected = naive(x0, f); + if (got != expected) cerr << "error: " << got.first << " " << got.second << " " << expected.first << " " << expected.second << FAIL; + queries += got.second; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr ll M = 18'086'183; +void performance_test() { + timer t; + auto f = [&](ll x){return (1337*x + 42) % M;}; + t.start(); + auto [a, b] = cycleDetection(42, f); + t.stop(); + hash_t hash = a + b; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/discreteLogarithm.cpp b/test/math/discreteLogarithm.cpp new file mode 100644 index 0000000..0f9eecf --- /dev/null +++ b/test/math/discreteLogarithm.cpp @@ -0,0 +1,64 @@ +#include "../util.h" +#include <math/modPowIterativ.cpp> +#include <math/legendre.cpp> + +ll overwrite = 0; +ll getMemory(ll /**/) {return overwrite - 1;} //dlog code adds one... +#define sqrtl getMemory +#include <math/discreteLogarithm.cpp> +#undef sqrtl + +template<typename F> +void stress_test(F&& f) { + ll work = 0; + for (ll tries = 0; tries < 3'000; tries++) { + ll p = Random::prime<ll>(1'000); + overwrite = f(p); + ll a = Random::integer<ll>(1, p); + vector<bool> naive(p); + for (ll i = 0, j = 1; i < p; i++, j = (j * a) % p) { + naive[j] = true; + } + for (ll b = 0; b < p; b++) { + ll got = dlog(a, b, p); + if (got < -1 || got >= p) cerr << "error: out of range" << FAIL; + if ((got >= 0) != naive[b]) { + cerr << a << " " << b << " " << p << endl; + cerr << got << endl; + cerr << "error" << FAIL; + } + if (got >= 0 && powMod(a, got, p) != b) { + cerr << a << "^" << got << " = " << powMod(a, got, p) << " != " << b << " % " << p << endl; + cerr << "error: wrong" << FAIL; + } + work++; + } + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 25; +constexpr ll mod = 1'394'633'899; +void performance_test() { + timer t; + hash_t hash = 0; + overwrite = sqrt(mod); + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(1, mod); + ll b = Random::integer<ll>(0, mod); + t.start(); + hash += dlog(a, b, mod); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test([](ll p){return sqrtl(p);}); + stress_test([](ll p){return min<ll>(10, p - 1);}); + stress_test([](ll p){return min<ll>(p - 1, sqrtl(p) + 100);}); + performance_test(); +} + diff --git a/test/math/discreteNthRoot.cpp b/test/math/discreteNthRoot.cpp new file mode 100644 index 0000000..d595e6d --- /dev/null +++ b/test/math/discreteNthRoot.cpp @@ -0,0 +1,78 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/millerRabin.cpp> +#include <math/rho.cpp> + +ll phi(ll pk, ll p, ll /*k*/) {return pk - pk / p;} +ll phi(ll n) { // O(sqrt(n)) + ll res = 1; + for (ll p = 2; p * p <= n; p++) { + if (n % p == 0) { + ll pk = 1; + ll k = 0; + do { + n /= p; + pk *= p; + k++; + } while (n % p == 0); + res *= phi(pk, p, k); + }} + if (n > 1) res *= phi(n, n, 1); + return res; +} + +#include <math/primitiveRoot.cpp> +#include <math/discreteLogarithm.cpp> +#include <math/discreteNthRoot.cpp> + +//x^a=b mod m +ll naiveRoot(ll a, ll b, ll m) { + for (ll i = 0; i < m; i++) { + if (powMod(i, a, m) == b) return i; + } + return -1; +} + +void stress_test() { + int queries = 0; + int found = 0; + for (int tries = 0; tries < 50'000; tries++) { + ll p = Random::prime<ll>(0, 1000); + ll a = Random::integer<ll>(1, p); + ll b = Random::integer<ll>(1, p); + + ll got = root(a, b, p); + ll expected = naiveRoot(a, b, p); + + if (got < -1 || got >= p) cerr << "error: out of range" << FAIL; + if (got >= 0 && powMod(got, a, p) != b) cerr << "error: wrong" << FAIL; + if ((got >= 0) != (expected >= 0)) cerr << "error" << FAIL; + queries++; + if (expected >= 0) found++; + } + cerr << "tested random queries: " << queries << " (" << found << ")" << endl; +} + +constexpr int N = 50; +constexpr ll mod = 1'394'633'899; +void performance_test() { + timer t; + hash_t hash = 0; + for (int i = 0; i < N; i++) { + ll a = Random::integer<ll>(1, mod); + ll b = Random::integer<ll>(1, mod); + t.start(); + hash += root(a, b, mod); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/divisors.cpp b/test/math/divisors.cpp new file mode 100644 index 0000000..2402d2a --- /dev/null +++ b/test/math/divisors.cpp @@ -0,0 +1,65 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/millerRabin.cpp> + +bool isSquare(ll x) { + ll r = sqrtl(x); + while (r*r > x) r--; + while ((r+1)*(r+1) <= x) r++; + return r*r==x; +} + +#include <math/divisors.cpp> + +ll naive(ll x) { + ll res = 0; + for (ll i = 1; i*i <= x; i++) { + if (x % i == 0) { + res++; + if (i*i != x) res++; + } + } + return res; +} + +void stress_test() { + ll work = 0; + for (ll i = 0; i < 1'000; i++) { + ll x = Random::integer<ll>(1, 1'000'000'000'000); + auto got = countDivisors(x); + auto expected = naive(x); + if (got != expected) cerr << "error: " << x << FAIL; + work += sqrt(x); + } + for (ll i = 0; i < 100'000; i++) { + ll x = Random::integer<ll>(1, 1'000'000); + auto got = countDivisors(x); + auto expected = naive(x); + if (got != expected) cerr << "error: " << x << FAIL; + work += sqrt(x); + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 200; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll x = Random::integer<ll>(1e18 / 2, 1e18); + t.start(); + hash += countDivisors(x); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/extendedEuclid.cpp b/test/math/extendedEuclid.cpp new file mode 100644 index 0000000..597f722 --- /dev/null +++ b/test/math/extendedEuclid.cpp @@ -0,0 +1,41 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> + +void stress_test() { + if (extendedEuclid(0, 0)[0] != 0) cerr << "error: extendedEuclid(0, 0)" << FAIL; + ll queries = 0; + timer t; + for (int i = 0; i < 1'000'000; i++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = 0; + { + t.start(); + auto [got, x, y] = extendedEuclid(a, b); + t.stop(); + ll expected = std::gcd(a, b); + if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + if (abs(x) >= max<ll>(2, abs(b))) cerr << "invalid x" << FAIL; + if (abs(y) >= max<ll>(2, abs(a))) cerr << "invalid y" << FAIL; + if (a*x + b*y != expected) cerr << "invalid x or y" << FAIL; + } + b = Random::integer<ll>(0, 1'000'000'000); + { + t.start(); + auto [got, x, y] = extendedEuclid(a, b); + t.stop(); + ll expected = std::gcd(a, b); + if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + if (abs(x) >= max<ll>(2, abs(b))) cerr << "invalid x" << FAIL; + if (abs(y) >= max<ll>(2, abs(a))) cerr << "invalid y" << FAIL; + if (a*x + b*y != expected) cerr << "invalid x or y" << FAIL; + } + queries++; + } + cerr << "tested random queries: " << queries << endl; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms" << endl; +} + +int main() { + stress_test(); +} diff --git a/test/math/gauss.cpp b/test/math/gauss.cpp new file mode 100644 index 0000000..37bacce --- /dev/null +++ b/test/math/gauss.cpp @@ -0,0 +1,118 @@ +#include "../util.h" +constexpr double EPS = 1e-9; +constexpr int UNIQUE = 1; +constexpr int INCONSISTENT = 2; +constexpr int MULTIPLE = 3; +vector<vector<double>> mat; +#include <math/gauss.cpp> + +vector<vector<double>> inverseMat(const vector<vector<double>>& m) { + int n = sz(m); + mat = m; + for (int i = 0; i < n; i++) { + if (sz(mat[i]) != n) cerr << "error: no square matrix" << FAIL; + mat[i].resize(2*n); + mat[i][n+i] = 1; + } + gauss(n);//the unique cetc. checks are not usefull since we dont solve an lgs... + vector<vector<double>> res(m); + for (int i = 0; i < n; i++) { + res[i] = vector<double>(mat[i].begin() + n, mat[i].end()); + for (int j = 0; j < n; j++) { + if (j != i && mat[i][j] != 0) cerr << "error: not full rank?" << FAIL; + if (j == i && mat[i][j] == 0) cerr << "error: not full rank?" << FAIL; + } + } + return res; +} + +vector<vector<double>> mul(const vector<vector<double>>& a, const vector<vector<double>>& b) { + int n = sz(a); + int m = sz(b[0]); + int x = sz(b); + if (sz(a[0]) != sz(b)) cerr << "error: wrong dimensions" << FAIL; + vector<vector<double>> res(n, vector<double>(m)); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + for (int k = 0; k < x; k++) { + res[i][j] += a[i][k] * b[k][j]; + } + } + } + return res; +} + +void test_tiny() { + mat = { + {1, 2, 3, 4}, + {0, 5, 6, 7}, + {0, 0, 8, 9}, + }; + if (gauss(sz(mat)) != UNIQUE) cerr << "error: 1" << FAIL; + + mat = { + {-1, 1, 0, -1}, + { 2, 6, 0, 10}, + { 1, -2, 0, 0}, + }; + if (gauss(sz(mat)) != MULTIPLE) cerr << "error: 2" << FAIL; + + mat = { + {-1, 1, 0, -1}, + { 2, 6, 0, 10}, + { 1, -2, 0, 1}, + }; + if (gauss(sz(mat)) != INCONSISTENT) cerr << "error: 3" << FAIL; +} + +void stress_test_inv() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + int n = Random::integer<int>(1, 30); + + vector<vector<double>> m(n); + for (auto& v : m) v = Random::reals<double>(n, 0, 1'000); + // m hopefully has full rank... + + auto inv = inverseMat(m); + + auto prod = mul(m, inv); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j && abs(prod[i][j] - 1) >= EPS) cerr << "error: not inverted " << prod[i][j] << FAIL; + if (i != j && abs(prod[i][j] - 0) >= EPS) cerr << "error: not inverted " << prod[i][j] << FAIL; + } + } + + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 250; +void performance_test() { + timer t; + + vector<vector<double>> m(N); + for (auto& v : m) v = Random::reals<double>(N, 0, 1'000); + mat = m; + + t.start(); + gauss(N); + t.stop(); + hash_t hash = 0; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + hash += mat[i][j]; + } + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + test_tiny(); + stress_test_inv(); + performance_test(); +} diff --git a/test/math/gcd-lcm.cpp b/test/math/gcd-lcm.cpp new file mode 100644 index 0000000..294095b --- /dev/null +++ b/test/math/gcd-lcm.cpp @@ -0,0 +1,46 @@ +#include "../util.h" +#include <math/gcd-lcm.cpp> + +void stress_test() { + if (::gcd(0, 0) != 0) cerr << "error: gcd(0, 0)" << FAIL; + if (::lcm(0, 0) != 0) cerr << "error: lcm(0, 0)" << FAIL; + ll queries = 0; + timer t; + for (int i = 0; i < 1'000'000; i++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = 0; + { + ll got = ::gcd(a, b); + ll expected = std::gcd(a, b); + if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + } + { + ll got = ::lcm(a, b); + ll expected = std::lcm(a, b); + if (got != expected) cerr << "lcm(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + } + b = Random::integer<ll>(0, 1'000'000'000); + { + t.start(); + ll got = ::gcd(a, b); + t.stop(); + ll expected = std::gcd(a, b); + if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + } + { + t.start(); + ll got = ::lcm(a, b); + t.stop(); + ll expected = std::lcm(a, b); + if (got != expected) cerr << "lcm(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL; + } + queries++; + } + cerr << "tested random queries: " << queries << endl; + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms" << endl; +} + +int main() { + stress_test(); +} diff --git a/test/math/goldenSectionSearch.cpp b/test/math/goldenSectionSearch.cpp new file mode 100644 index 0000000..565a21c --- /dev/null +++ b/test/math/goldenSectionSearch.cpp @@ -0,0 +1,74 @@ +#include "../util.h" +#include <math/goldenSectionSearch.cpp> + +struct RandomFunction { + ld min; + vector<pair<ld, int>> polys; + RandomFunction(ld l, ld r) : min(Random::real<ld>(l, r)) { + do { + polys.emplace_back( + Random::real<ld>(0, 1e9), + 2 * Random::integer<int>(1, 5) + ); + } while(false && Random::integer<int>(4) != 0); + } + + ld operator()(ld x){ + ld res = 0; + for (auto [m, p] : polys) { + res += m * pow(x - min, p); + } + return res; + } + + friend ostream& operator<<(ostream& os, const RandomFunction& f) { + string plus = ""; + for (auto [m, p] : f.polys) { + os << setprecision(15) << plus << m << "*(x-" << f.min << ")**" << p; + plus = "+"; + } + return os; + } +}; + +void stress_test() { + int queries = 0; + for (int i = 0; i < 50'000; i++) { + ld l = Random::real<double>(-200, 200); + ld r = Random::real<double>(-200, 200); + if (l > r) swap(l, r); + + RandomFunction f(l, r); + + ld got = gss(l, r, f); + ld expected = f.min; + if (float_error(got, expected) > 1e-6) { + cerr << f << endl; + cerr << "got: " << got << ", expected: " << expected << FAIL; + } + queries++; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 10'000; +void performance_test() { + timer t; + RandomFunction f(-200, 200); + f.polys.resize(1); + + hash_t hash = 0; + for (int i = 0; i < N; i++) { + t.start(); + hash += gss(-200, 200, f); + t.stop(); + } + if (t.time > 1000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/inversions.cpp b/test/math/inversions.cpp new file mode 100644 index 0000000..d2a54b7 --- /dev/null +++ b/test/math/inversions.cpp @@ -0,0 +1,43 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> +#include <math/inversions.cpp> + +ll naive(const vector<ll>& v) { + ll res = 0; + for (ll i = 0; i < sz(v); i++) { + for (ll j = 0; j < i; j++) { + if (v[j] > v[i]) res++; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 100'000; i++) { + int n = Random::integer<int>(1, 100); + auto v = Random::integers<ll>(n, -50, 50); + ll got = inversions(v); + ll expected = naive(v); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 200'000; +void performance_test() { + timer t; + auto v = Random::integers<ll>(N, -10'000, 10'000); + t.start(); + hash_t hash = inversions(v); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/inversionsMerge.cpp b/test/math/inversionsMerge.cpp new file mode 100644 index 0000000..85ab0d2 --- /dev/null +++ b/test/math/inversionsMerge.cpp @@ -0,0 +1,46 @@ +#include "../util.h" +#include <math/inversionsMerge.cpp> + +ll naive(const vector<ll>& v) { + ll res = 0; + for (ll i = 0; i < sz(v); i++) { + for (ll j = 0; j < i; j++) { + if (v[j] > v[i]) res++; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 100'000; i++) { + int n = Random::integer<int>(1, 100); + vector<ll> v(n); + for (ll j = 0; j < n; j++) v[j] = (j-10) * 100000 + Random::integer<ll>(0, 10000);//values must be unique ): + shuffle(all(v), Random::rng); + ll expected = naive(v); + ll got = mergeSort(v); + if (got != expected) { + cerr << "got: " << got << ", expected: " << expected << FAIL; + } + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 2'000'000; //10 times faster +void performance_test() { + timer t; + auto v = Random::integers<ll>(N, -10'000, 10'000); + t.start(); + hash_t hash = mergeSort(v); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/kthperm.cpp b/test/math/kthperm.cpp new file mode 100644 index 0000000..16691b9 --- /dev/null +++ b/test/math/kthperm.cpp @@ -0,0 +1,38 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> +#include <math/kthperm.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + vector<ll> expected(n); + iota(all(expected), 0); + ll k = 0; + do { + auto got = kthperm(n, k); + if (got != expected) cerr << "error" << FAIL; + k++; + } while (k < 100 && next_permutation(all(expected))); + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + t.start(); + auto got = kthperm(N, 4'168'751'907'498'170ll); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * got[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/kthperm_permIndex.cpp b/test/math/kthperm_permIndex.cpp new file mode 100644 index 0000000..d84524e --- /dev/null +++ b/test/math/kthperm_permIndex.cpp @@ -0,0 +1,21 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> +#include <math/kthperm.cpp> +#include <math/permIndex.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + ll n = Random::integer<ll>(20, 1000); + ll expected = Random::integer<ll>(0, 1'000'000'000'000'000'000); + ll got = permIndex(kthperm(n, expected)); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/legendre.cpp b/test/math/legendre.cpp new file mode 100644 index 0000000..f210b57 --- /dev/null +++ b/test/math/legendre.cpp @@ -0,0 +1,43 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/legendre.cpp> + +void stress_test() { + ll work = 0; + for (ll i = 0; i < 5'000; i++) { + ll p = Random::prime<ll>(5'000); + vector<bool> isSquare(p); + for (ll j = 1; j < p; j++) isSquare[(j*j) % p] = true; + for (ll j = 0; j < p; j++) { + auto got = legendre(j, p); + auto expected = j == 0 ? 0 : (isSquare[j] ? 1 : -1); + if (got != expected) cerr << "error: " << j << " " << p << FAIL; + } + work += p; + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 1'000'000; +constexpr ll mod = 1'394'633'899; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll j = Random::integer<ll>(mod); + t.start(); + hash += legendre(j, mod); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/lgsFp.cpp b/test/math/lgsFp.cpp new file mode 100644 index 0000000..08f8f84 --- /dev/null +++ b/test/math/lgsFp.cpp @@ -0,0 +1,118 @@ +#include "../util.h" +#include <math/shortModInv.cpp> +vector<vector<ll>> mat; +#include <math/lgsFp.cpp> + +constexpr ll mod = 1'000'000'007; + +vector<vector<ll>> inverseMat(const vector<vector<ll>>& m) { + int n = sz(m); + mat = m; + for (int i = 0; i < n; i++) { + if (sz(mat[i]) != n) cerr << "error: no square matrix" << FAIL; + mat[i].resize(2*n); + mat[i][n+i] = 1; + } + gauss(n, mod); + vector<vector<ll>> res(m); + for (int i = 0; i < n; i++) { + res[i] = vector<ll>(mat[i].begin() + n, mat[i].end()); + for (int j = 0; j < n; j++) { + if (j != i && mat[i][j] != 0) cerr << "error: not full rank?" << FAIL; + if (j == i && mat[i][j] != 1) cerr << "error: not full rank?" << FAIL; + } + } + return res; +} + +vector<vector<ll>> mul(const vector<vector<ll>>& a, const vector<vector<ll>>& b) { + int n = sz(a); + int m = sz(b[0]); + int x = sz(b); + if (sz(a[0]) != sz(b)) cerr << "error: wrong dimensions" << FAIL; + vector<vector<ll>> res(n, vector<ll>(m)); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + for (int k = 0; k < x; k++) { + res[i][j] += a[i][k] * b[k][j]; + res[i][j] %= mod; + } + } + } + return res; +} + +//this should just not crash... +void test_square() { + ll queries = 0; + hash_t hash = 0; + for (int tries = 0; tries < 1'000; tries++) { + int n = Random::integer<int>(1, 30); + + vector<vector<ll>> m(n); + for (auto& v : m) v = Random::integers<ll>(n, 0, mod); + mat = m; + gauss(n, mod); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + hash += mat[i][j]; + } + } + + queries += n; + } + cerr << "tested sqaures: " << queries << " (hash: " << hash << ")" << endl;; +} + +void stress_test_inv() { + ll queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + int n = Random::integer<int>(1, 30); + + vector<vector<ll>> m(n); + for (auto& v : m) v = Random::integers<ll>(n, 0, mod); + // m hopefully has full rank... + + auto inv = inverseMat(m); + + auto prod = mul(m, inv); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j && prod[i][j] != 1) cerr << "error: not inverted" << FAIL; + if (i != j && prod[i][j] != 0) cerr << "error: not inverted" << FAIL; + } + } + + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 250; +void performance_test() { + timer t; + + vector<vector<ll>> m(N); + for (auto& v : m) v = Random::integers<ll>(N, 0, mod); + mat = m; + + t.start(); + gauss(N, mod); + t.stop(); + hash_t hash = 0; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + hash += mat[i][j]; + } + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + test_square(); + stress_test_inv(); + performance_test(); +} diff --git a/test/math/linearCongruence.cpp b/test/math/linearCongruence.cpp new file mode 100644 index 0000000..ba8eeac --- /dev/null +++ b/test/math/linearCongruence.cpp @@ -0,0 +1,53 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> +#include <math/multInv.cpp> +#include <math/linearCongruence.cpp> + +ll naive(ll a, ll b, ll m) { + for (ll x = 0; x < m; x++) { + if ((a * x) % m == b) return x; + } + return -1; +} + +void stress_test() { + ll work = 0; + ll positive = 0; + for (ll tries = 0; tries < 500'000; tries++) { + ll m = Random::integer<ll>(0, 1'000); + ll a = Random::integer<ll>(0, m); + ll b = Random::integer<ll>(0, m); + + ll got = solveLinearCongruence(a, b, m); + ll expected = naive(a, b, m); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << endl; + work++; + if (got >= 0) positive++; + } + cerr << "stress tested: " << work << " (" << positive << ")" << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll m = Random::integer<ll>(0, 1'0000'000'000); + ll a = Random::integer<ll>(0, m); + ll b = Random::integer<ll>(0, m); + + t.start(); + hash += solveLinearCongruence(a, b, m); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/linearRecurence.cpp b/test/math/linearRecurence.cpp new file mode 100644 index 0000000..a5290e5 --- /dev/null +++ b/test/math/linearRecurence.cpp @@ -0,0 +1,54 @@ +#include "../util.h" +#include <math/linearRecurence.cpp> + +struct RandomRecurence { + vector<ll> f, c, cache; + RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {} + + ll operator()(ll k){ + while (sz(cache) <= k) { + ll cur = 0; + for (ll i = 0; i < sz(c); i++) { + cur += (c[i] * cache[sz(cache) - i - 1]) % mod; + } + cur %= mod; + cache.push_back(cur); + } + return cache[k]; + } +}; + +void stress_test() { + int queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 10); + RandomRecurence f(n); + for (int j = 0; j < 100; j++) { + ll k = Random::integer<ll>(0, 1000); + + ll got = kthTerm(f.f, f.c, k); + ll expected = f(k); + + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000; +void performance_test() { + timer t; + RandomRecurence f(N); + t.start(); + hash_t hash = kthTerm(f.f, f.c, 1e18); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/linearSieve.cpp b/test/math/linearSieve.cpp new file mode 100644 index 0000000..8ea822b --- /dev/null +++ b/test/math/linearSieve.cpp @@ -0,0 +1,71 @@ +#include "../util.h" +namespace expected { +#include <math/primeSieve.cpp> +} +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include <math/linearSieve.cpp> + +void stress_test() { + expected::primeSieve(); + expected::primes.resize(primes.size()); + if (expected::primes != primes) cerr << "error: primes" << FAIL; + int queries = 0; + for (int i = 1; i < 1'000'000; i++) { + auto got = sieved[i]; + auto expected = naive(i); + if (got != expected) cerr << i << ", got: " << got << ", expected: " << expected << FAIL; + queries++; + } + for (int i = 0; i < 1'000'000; i++) { + ll x = Random::integer<ll>(2, N); + auto got = sieved[x]; + auto expected = naive(x); + if (got != expected) cerr << x << ", got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested queries: " << queries << endl; +} + +void test_tiny() { + if (mu( 3, 3, 1) != -1) cerr << "error: 1" << FAIL; + if (mu( 9, 3, 2) != 0) cerr << "error: 2" << FAIL; + if (mu(27, 3, 3) != 0) cerr << "error: 3" << FAIL; + + if (phi( 3, 3, 1) != 2) cerr << "error: 4" << FAIL; + if (phi( 9, 3, 2) != 6) cerr << "error: 5" << FAIL; + if (phi(27, 3, 3) != 18) cerr << "error: 6" << FAIL; + + if (div( 3, 3, 1) != 2) cerr << "error: 7" << FAIL; + if (div( 9, 3, 2) != 3) cerr << "error: 8" << FAIL; + if (div(27, 3, 3) != 4) cerr << "error: 9" << FAIL; + + if (divSum( 3, 3, 1) != 4) cerr << "error: 10" << FAIL; + if (divSum( 9, 3, 2) != 13) cerr << "error: 11" << FAIL; + if (divSum(27, 3, 3) != 40) cerr << "error: 12" << FAIL; + + if (square( 3, 3, 1) != 1) cerr << "error: 13" << FAIL; + if (square( 9, 3, 2) != 9) cerr << "error: 14" << FAIL; + if (square(27, 3, 3) != 9) cerr << "error: 15" << FAIL; + + if (squareFree( 3, 3, 1) != 3) cerr << "error: 13" << FAIL; + if (squareFree( 9, 3, 2) != 3) cerr << "error: 14" << FAIL; + if (squareFree(27, 3, 3) != 3) cerr << "error: 15" << FAIL; + cerr << "tested tiny" << endl; +} + +void performance_test() { + timer t; + t.start(); + sieve(); + hash_t hash = sz(primes); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + performance_test(); + stress_test(); + test_tiny(); +} + diff --git a/test/math/longestIncreasingSubsequence.cpp b/test/math/longestIncreasingSubsequence.cpp new file mode 100644 index 0000000..407dafe --- /dev/null +++ b/test/math/longestIncreasingSubsequence.cpp @@ -0,0 +1,76 @@ +#include "../util.h" +constexpr ll INF = LL::INF; +#include <math/longestIncreasingSubsequence.cpp> +#define lis unstrictLis +#define lower_bound upper_bound +#include <math/longestIncreasingSubsequence.cpp> +#undef lower_bound +#undef lis + +template<bool STRICT> +bool isLis(const vector<ll>& a, const vector<int>& lis) { + for (int i = 1; i < sz(lis); i++) { + if (lis[i-1] >= lis[i]) return false; + if (a[lis[i-1]] > a[lis[i]]) return false; + if (STRICT && a[lis[i-1]] == a[lis[i]]) return false; + } + return true; +} + +template<bool STRICT> +vector<int> naive(const vector<ll>& a) { + vector<int> res; + for (ll i = 1; i < (1ll << sz(a)); i++) { + vector<int> tmp; + for (ll j = 0; j < sz(a); j++) { + if (((i >> j) & 1) != 0) tmp.push_back(j); + } + if (sz(tmp) >= sz(res) && isLis<STRICT>(a, tmp)) res = tmp; + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 12); + auto a = Random::integers<ll>(n, -10, 10); + auto expected = naive<true>(a); + auto got = lis(a); + if (got != expected) cerr << "error: strict" << FAIL; + queries += n; + } + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 12); + auto a = Random::integers<ll>(n, -10, 10); + auto expected = naive<false>(a); + auto got = unstrictLis(a); + if (got != expected) cerr << "error: not strict" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + auto a = Random::integers<ll>(N, -10'000, 10'000); + auto b = Random::integers<ll>(N, -10'000, 10'000); + sort(all(b)); + auto c = Random::integers<ll>(N, -10'000, 10'000); + sort(all(c)); + reverse(all(c)); + hash_t hash = 0; + t.start(); + hash += lis(a).size(); + hash += lis(b).size(); + hash += lis(c).size(); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/math/matrixPower.cpp b/test/math/matrixPower.cpp new file mode 100644 index 0000000..4dfb0a8 --- /dev/null +++ b/test/math/matrixPower.cpp @@ -0,0 +1,116 @@ +#include "../util.h" + +constexpr ll mod = 1'394'633'899; + +struct mat { + vector<vector<ll>> m; + mat(int dim = 0, int diag = 1) : m(dim, vector<ll>(dim)) { + for (int i = 0; i < dim; i++) m[i][i] = diag; + } + mat(const vector<ll> c) : m(sz(c), vector<ll>(sz(c))) { + m[0] = c; + for (ll i = 1; i < sz(c); i++) { + m[i][i-1] = 1; + } + } + + mat operator*(const mat& o) const { + int dim = sz(m); + mat res(dim, 0); + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + for (int k = 0; k < dim; k++) { + res.m[i][j] += m[i][k] * o.m[k][j]; + res.m[i][j] %= mod; + } + } + } + return res; + } + + vector<ll> operator*(const vector<ll>& o) const { + int dim = sz(m); + vector<ll> res(dim); + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + res[i] += m[i][j] * o[j]; + res[i] %= mod; + } + } + return res; + } +}; + +#include <math/matrixPower.cpp> + +struct RandomRecurence { + vector<ll> f, c, cache; + RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {} + + ll operator()(ll k){ + while (sz(cache) <= k) { + ll cur = 0; + for (ll i = 0; i < sz(c); i++) { + cur += (c[i] * cache[sz(cache) - i - 1]) % mod; + } + cur %= mod; + cache.push_back(cur); + } + return cache[k]; + } +}; + +void stress_test() { + int queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 10); + RandomRecurence f(n); + precalc(mat(f.c)); + auto tmp = f.f; + reverse(all(tmp)); + + for (int j = 0; j < 100; j++) { + ll k = Random::integer<ll>(0, 1000); + + vector<ll> got = calc(k, tmp); + vector<ll> expected(sz(f.f)); + for (ll l = 0; l < n; l++) expected[n - 1 - l] = f(k + l); + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 100; +constexpr int M = 500; +void performance_test() { + timer t; + RandomRecurence f(N); + auto tmp = f.f; + reverse(all(tmp)); + + t.start(); + precalc(mat(f.c)); + t.stop(); + if (t.time > 500) cerr << "too slow precalc: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms" << endl; + + t.reset(); + hash_t hash = 0; + for (int i = 0; i < M; i++) { + ll k = Random::integer<ll>(1e17,1e18); + t.start(); + hash += calc(k, tmp).back(); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/millerRabin.base32.cpp b/test/math/millerRabin.base32.cpp new file mode 100644 index 0000000..742d353 --- /dev/null +++ b/test/math/millerRabin.base32.cpp @@ -0,0 +1,137 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll + +//this is hacky... +#define bool }\ +constexpr auto bases64 = c20::to_array(ignore::bases32);\ +bool +namespace ignore { +#include <math/millerRabin.cpp> +#undef bool + +bool naive(ll x) { + for (ll i = 2; i*i <= x; i++) { + if (x % i == 0) return false; + } + return x > 1; +} + +ll mul(const map<ll, int>& facts) { + ll res = 1; + for (auto [p, c] : facts) { + for (int i = 0; i < c; i++) res *= p; + } + if (abs(res) > (1ll << 62)) cerr << "invalid number: " << res << FAIL; + return res; +} + +void extra_tests() { + vector<map<ll, int>> test = { + {{-1, 1}, {1, 1}}, + {{-2, 1}, {1, 1}}, + {{-7, 1}, {1, 1}}, + {{-19812365821, 1}, {1, 1}}, + {}, // 1 + {{2, 1}}, + {{3, 1}}, + {{2, 2}}, + {{5, 1}}, + {{2, 1}, {3, 1}}, + {{2, 2}, {3, 1}}, + {{2, 1}, {3, 2}}, + {{2, 2}, {3, 2}}, + {{2, 62}}, + {{2, 18}, {5, 18}}, + {{352523, 1}, {352817, 1}}, + {{41, 1}, {71, 1}, {421, 1}, {811, 1}}, + {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}}, + {{500000003, 1}, {1999999973, 1}}, + {{65537, 2}}, + {{999665081, 1}, {999716071, 1}}, + {{550177, 1}, {1100353, 1}, {1650529, 1}}, + {{459397, 1}, {918793, 1}, {1378189, 1}}, + {{37, 1}, {109, 1}}, + {{31, 1}, {151, 1}}, + {{239, 1}, {1429, 1}}, + {{89, 1}, {1093, 1}}, + {{2, 3}, {15800133918749317, 1}}, + {{12251, 1}, {85751, 1}}, + {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}}, + {{5, 1}, {1927962474784631, 1}}, + {{197279, 1}, {1775503, 1}}, + {{3716371, 1}, {14865481, 1}}, + {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}}, + {{4880401, 1}, {9760801, 1}}, + {{2822159, 1}, {11288633, 1}}, + {{3290341, 1}, {6580681, 1}}, + {{611557, 1}, {1834669, 1}}, + {{9227, 1}, {894923, 1}, {968731, 1}}, + {{3, 4}, {13, 1}, {62633, 2}}, + {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}}, + {{332721269, 1}, {560937673, 1}}, + {{30702523, 1}, {122810089, 1}}, + {{24786439, 1}, {123932191, 1}}, + {{382500329, 1}, {1530001313, 1}}, + {{2, 4}, {5, 4}, {13, 1}, {30839, 2}}, + {{3, 1}, {385417, 1}, {7985344259, 1}}, + {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}}, + {{627838711, 1}, {1212379867, 1}}, + {{3, 5}, {5, 3}, {41, 2}, {157321, 2}}, + {{5, 2}, {13, 1}}, + {{3, 1}, {5, 5}}, + {{2, 1}, {73, 1}, {193, 1}}, + {{5, 2}, {13, 1}, {19, 1}, {73, 1}}, + {{2, 3}, {3, 1}, {407521, 1}}, + {{2, 1}, {3, 1}, {299210837, 1}}, + {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}}, + }; + + timer t; + for (auto factors : test) { + ll x = mul(factors); + if (x >= 1ll << 32) continue; + t.start(); + auto got = isPrime(x); + t.stop(); + bool expected = sz(factors) == 1 && factors.begin()->second == 1; + if (got != expected) cerr << "error: " << x << FAIL; + } + if (t.time > 10) cerr << "too slow" << FAIL; + cerr << "stress tested: " << t.time << "ms" << endl; +} + +void stress_test() { + ll work = 0; + for (ll i = 0; i < 10'000; i++) { + ll x = Random::integer<ll>(1, 1ll << 32); + auto got = isPrime(x); + auto expected = naive(x); + if (got != expected) cerr << "error: " << x << FAIL; + work += sqrt(x); + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 200'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll x = Random::integer<ll>(1ll << 31, 1ll << 32); + t.start(); + hash += isPrime(x); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + extra_tests(); + stress_test(); + performance_test(); +} + diff --git a/test/math/millerRabin.cpp b/test/math/millerRabin.cpp new file mode 100644 index 0000000..fd98586 --- /dev/null +++ b/test/math/millerRabin.cpp @@ -0,0 +1,129 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/millerRabin.cpp> + +bool naive(ll x) { + for (ll i = 2; i*i <= x; i++) { + if (x % i == 0) return false; + } + return x > 1; +} + +ll mul(const map<ll, int>& facts) { + ll res = 1; + for (auto [p, c] : facts) { + for (int i = 0; i < c; i++) res *= p; + } + if (abs(res) > (1ll << 62)) cerr << "invalid number: " << res << FAIL; + return res; +} + +void extra_tests() { + vector<map<ll, int>> test = { + {{-1, 1}, {1, 1}}, + {{-2, 1}, {1, 1}}, + {{-7, 1}, {1, 1}}, + {{-19812365821, 1}, {1, 1}}, + {}, // 1 + {{2, 1}}, + {{3, 1}}, + {{2, 2}}, + {{5, 1}}, + {{2, 1}, {3, 1}}, + {{2, 2}, {3, 1}}, + {{2, 1}, {3, 2}}, + {{2, 2}, {3, 2}}, + {{2, 62}}, + {{2, 18}, {5, 18}}, + {{352523, 1}, {352817, 1}}, + {{41, 1}, {71, 1}, {421, 1}, {811, 1}}, + {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}}, + {{500000003, 1}, {1999999973, 1}}, + {{65537, 2}}, + {{999665081, 1}, {999716071, 1}}, + {{550177, 1}, {1100353, 1}, {1650529, 1}}, + {{459397, 1}, {918793, 1}, {1378189, 1}}, + {{37, 1}, {109, 1}}, + {{31, 1}, {151, 1}}, + {{239, 1}, {1429, 1}}, + {{89, 1}, {1093, 1}}, + {{2, 3}, {15800133918749317, 1}}, + {{12251, 1}, {85751, 1}}, + {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}}, + {{5, 1}, {1927962474784631, 1}}, + {{197279, 1}, {1775503, 1}}, + {{3716371, 1}, {14865481, 1}}, + {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}}, + {{4880401, 1}, {9760801, 1}}, + {{2822159, 1}, {11288633, 1}}, + {{3290341, 1}, {6580681, 1}}, + {{611557, 1}, {1834669, 1}}, + {{9227, 1}, {894923, 1}, {968731, 1}}, + {{3, 4}, {13, 1}, {62633, 2}}, + {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}}, + {{332721269, 1}, {560937673, 1}}, + {{30702523, 1}, {122810089, 1}}, + {{24786439, 1}, {123932191, 1}}, + {{382500329, 1}, {1530001313, 1}}, + {{2, 4}, {5, 4}, {13, 1}, {30839, 2}}, + {{3, 1}, {385417, 1}, {7985344259, 1}}, + {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}}, + {{627838711, 1}, {1212379867, 1}}, + {{3, 5}, {5, 3}, {41, 2}, {157321, 2}}, + {{5, 2}, {13, 1}}, + {{3, 1}, {5, 5}}, + {{2, 1}, {73, 1}, {193, 1}}, + {{5, 2}, {13, 1}, {19, 1}, {73, 1}}, + {{2, 3}, {3, 1}, {407521, 1}}, + {{2, 1}, {3, 1}, {299210837, 1}}, + {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}}, + }; + + timer t; + for (auto factors : test) { + ll x = mul(factors); + t.start(); + auto got = isPrime(x); + t.stop(); + bool expected = sz(factors) == 1 && factors.begin()->second == 1; + if (got != expected) cerr << "error: " << x << FAIL; + } + if (t.time > 10) cerr << "too slow" << FAIL; + cerr << "stress tested: " << t.time << "ms" << endl; +} + +void stress_test() { + ll work = 0; + for (ll i = 0; i < 10'000; i++) { + ll x = Random::integer<ll>(1, 1'000'000'000'000); + auto got = isPrime(x); + auto expected = naive(x); + if (got != expected) cerr << "error: " << x << FAIL; + work += sqrt(x); + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 200'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll x = Random::integer<ll>(1e18 / 2, 1e18); + t.start(); + hash += isPrime(x); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + extra_tests(); + stress_test(); + performance_test(); +} + diff --git a/test/math/modExp.cpp b/test/math/modExp.cpp new file mode 100644 index 0000000..ebb38eb --- /dev/null +++ b/test/math/modExp.cpp @@ -0,0 +1,42 @@ +#include "../util.h" +#include <math/modExp.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int a = Random::integer<int>(1, 100); + int n = Random::integer<int>(2, 100); + ll expected = 1; + ll k = 0; + do { + auto got = powMod(a, k, n); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + k++; + expected = (expected * a) % n; + } while (k < 100); + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = Random::integer<ll>(0, 1'000'000'000); + ll n = Random::integer<ll>(2, 1'000'000'000); + t.start(); + hash += powMod(a, b, n); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/modMulIterativ.cpp b/test/math/modMulIterativ.cpp new file mode 100644 index 0000000..4f794c5 --- /dev/null +++ b/test/math/modMulIterativ.cpp @@ -0,0 +1,57 @@ +#include "../util.h" +#include <math/modMulIterativ.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int a = Random::integer<int>(1, 100); + int n = Random::integer<int>(2, 100); + ll expected = 0; + ll k = 0; + do { + auto got = mulMod(a, k, n); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + k++; + expected = (expected + a) % n; + } while (k < 100); + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +void stress_test_large() { + ll queries = 0; + for (ll i = 0; i < 1000'000; i++) { + ll a = Random::integer<ll>(0, 1'000'000'000'000'000'000); + ll b = Random::integer<ll>(0, 1'000'000'000'000'000'000); + ll n = Random::integer<ll>(2, 1'000'000'000'000'000'000); + ll expected = (lll)a * b % n; + auto got = mulMod(a, b, n); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(0, 1'000'000'000'000'000'000); + ll b = Random::integer<ll>(0, 1'000'000'000'000'000'000); + ll n = Random::integer<ll>(2, 1'000'000'000'000'000'000); + t.start(); + hash += mulMod(a, b, n); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + stress_test_large(); + performance_test(); +} + diff --git a/test/math/modPowIterativ.cpp b/test/math/modPowIterativ.cpp new file mode 100644 index 0000000..2cf0eb4 --- /dev/null +++ b/test/math/modPowIterativ.cpp @@ -0,0 +1,42 @@ +#include "../util.h" +#include <math/modPowIterativ.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int a = Random::integer<int>(1, 100); + int n = Random::integer<int>(2, 100); + ll expected = 1; + ll k = 0; + do { + auto got = powMod(a, k, n); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + k++; + expected = (expected * a) % n; + } while (k < 100); + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = Random::integer<ll>(0, 1'000'000'000); + ll n = Random::integer<ll>(2, 1'000'000'000); + t.start(); + hash += powMod(a, b, n); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/multInv.cpp b/test/math/multInv.cpp new file mode 100644 index 0000000..93763c5 --- /dev/null +++ b/test/math/multInv.cpp @@ -0,0 +1,40 @@ +#include "../util.h" +#include <math/extendedEuclid.cpp> +#include <math/multInv.cpp> + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 10'000'000; i++) { + ll n = Random::integer<ll>(2, 1'000'000'000); + ll x = 0; + do { + x = Random::integer<ll>(0, n); + } while (gcd(x, n) != 1); + ll y = multInv(x, n); + ll got = (x*y) % n; + if (got != 1) cerr << "got: " << got << ", expected: 1" << FAIL; + queries++; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = Random::integer<ll>(2, 1'000'000'000); + t.start(); + hash += multInv(a, b); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/permIndex.cpp b/test/math/permIndex.cpp new file mode 100644 index 0000000..61d34c8 --- /dev/null +++ b/test/math/permIndex.cpp @@ -0,0 +1,39 @@ +#include "../util.h" +#include <datastructures/pbds.cpp> +#include <math/permIndex.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + vector<ll> cur(n); + iota(all(cur), 0); + ll expected = 0; + do { + auto got = permIndex(cur); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + expected++; + } while (expected < 100 && next_permutation(all(cur))); + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + vector<ll> cur(N); + iota(all(cur), 0); + reverse(cur.end() - 10, cur.end()); + t.start(); + auto hash = permIndex(cur); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/piLegendre.cpp b/test/math/piLegendre.cpp new file mode 100644 index 0000000..c3513bf --- /dev/null +++ b/test/math/piLegendre.cpp @@ -0,0 +1,40 @@ +#include "../util.h" +#include <math/primeSieve.cpp> +namespace legendre { + #include <math/piLegendre.cpp> +} +namespace lehmer { + #include <math/piLehmer.cpp> +} + +void stress_test() { + int queries = 0; + for (int i = 0; i < 1'000; i++) { + ll x = Random::integer<ll>(0, 1'000'000'000); + auto got = legendre::pi(x); + auto expected = lehmer::pi(x); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test() { + timer t; + hash_t hash = 0; + for (int i = 0; i < 1; i++) { + ll x = Random::integer<ll>(0, 1000'000'000'000); + t.start(); + hash += legendre::pi(x); + t.stop(); + } + if (t.time > 1500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + lehmer::init(); + performance_test(); + stress_test(); +} + diff --git a/test/math/piLehmer.cpp b/test/math/piLehmer.cpp new file mode 100644 index 0000000..d84466f --- /dev/null +++ b/test/math/piLehmer.cpp @@ -0,0 +1,42 @@ +#include "../util.h" +#include <math/primeSieve.cpp> +namespace legendre { + #include <math/piLegendre.cpp> +} +namespace lehmer { + #include <math/piLehmer.cpp> +} + +void stress_test() { + int queries = 0; + for (int i = 0; i < 1'000; i++) { + ll x = Random::integer<ll>(0, 1'000'000'000); + auto got = lehmer::pi(x); + auto expected = legendre::pi(x); + if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test() { + timer t; + hash_t hash = 0; + t.start(); + lehmer::init(); + t.stop(); + for (int i = 0; i < 1; i++) { + ll x = Random::integer<ll>(0, 1000'000'000'000); + t.start(); + hash += lehmer::pi(x); + t.stop(); + } + if (t.time > 1500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + performance_test(); + stress_test(); +} + diff --git a/test/math/primeSieve.cpp b/test/math/primeSieve.cpp new file mode 100644 index 0000000..78a50d2 --- /dev/null +++ b/test/math/primeSieve.cpp @@ -0,0 +1,47 @@ +#include "../util.h" +#include <math/primeSieve.cpp> + +bool naive(ll x) { + for (ll i = 2; i*i <= x; i++) { + if (x % i == 0) return false; + } + return x > 1; +} + +void stress_test() { + int queries = 0; + vector<ll> found; + for (int i = -5; i < 1'000'000; i++) { + auto got = isPrime(i); + auto expected = naive(i); + if (got != expected) cerr << "error: " << i << FAIL; + if (got) found.push_back(i); + queries++; + } + primes.resize(sz(found)); + if (primes != found) cerr << "error: primes" << FAIL; + for (int i = 0; i < 1'000'000; i++) { + ll x = Random::integer<ll>(2, N); + auto got = isPrime(x); + auto expected = naive(x); + if (got != expected) cerr << "error: " << x << FAIL; + queries++; + } + cerr << "tested queries: " << queries << endl; +} + +void performance_test() { + timer t; + t.start(); + primeSieve(); + hash_t hash = sz(primes); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + performance_test(); + stress_test(); +} + diff --git a/test/math/primitiveRoot.cpp b/test/math/primitiveRoot.cpp new file mode 100644 index 0000000..cd0b388 --- /dev/null +++ b/test/math/primitiveRoot.cpp @@ -0,0 +1,82 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/millerRabin.cpp> +#include <math/rho.cpp> + +ll phi(ll pk, ll p, ll /*k*/) {return pk - pk / p;} +ll phi(ll n) { // O(sqrt(n)) + ll res = 1; + for (ll p = 2; p * p <= n; p++) { + if (n % p == 0) { + ll pk = 1; + ll k = 0; + do { + n /= p; + pk *= p; + k++; + } while (n % p == 0); + res *= phi(pk, p, k); + }} + if (n > 1) res *= phi(n, n, 1); + return res; +} + +#include <math/primitiveRoot.cpp> + +bool naiveIsPrimitive(ll g, ll n) { + if (gcd(g, n) != 1) return false; + vector<bool> seen(n); + ll c = g; + for (ll i = 0; i < n; i++) { + seen[c] = true; + c = (c * g) % n; + } + ll res = 0; + for (bool x : seen) if (x) res++; + return res == phi(n); +} + +void stress_test() { + int queries = 0; + for (int tries = 0; tries < 20'000; tries++) { + ll a = Random::integer<ll>(1, 3); + ll p = Random::prime<ll>(0, 1000); + ll k = p == 2 ? 1 : Random::integer<ll>(1, log(100'000) / log(p) + 1); + + ll x = a; + for (int i = 0; i < k; i++) x *= p; + + ll got = findPrimitive(x); + + if (got < 0 || got >= x) cerr << "error: out of range" << FAIL; + if (!naiveIsPrimitive(got, x)) cerr << "error: wrong" << got << " " << x << FAIL; + queries++; + } + cerr << "tested random queries: " << queries << endl; +} + +void stress_test2() { + int queries = 0; + for (int x = 2; x < 5'000; x++) { + map<ll, int> facts; + factor(x, facts); + if (x % 2 == 0) facts.erase(facts.find(2)); + bool expected = sz(facts) == 1; + if (x % 4 == 0) expected = false; + if (x == 2 || x == 4) expected = true; + + bool got = findPrimitive(x) >= 0; + + if (got != expected) cerr << "error" << FAIL; + queries++; + } + cerr << "tested random queries: " << queries << endl; +} + +int main() { + stress_test(); + stress_test2(); +} + diff --git a/test/math/rho.cpp b/test/math/rho.cpp new file mode 100644 index 0000000..5e4792a --- /dev/null +++ b/test/math/rho.cpp @@ -0,0 +1,117 @@ +#include "../util.h" +#define ll lll +#include <math/modPowIterativ.cpp> +#undef ll +#include <math/millerRabin.cpp> +#include <math/rho.cpp> + +map<ll, int> factor(ll n) { + map<ll, int> facts; + factor(n, facts); + return facts; +} + +ll mul(const map<ll, int>& facts) { + ll res = 1; + for (auto [p, c] : facts) { + for (int i = 0; i < c; i++) res *= p; + } + if (res < 1 || res > (1ll << 62)) cerr << "invalid number: " << res << FAIL; + return res; +} + +void stress_test() { + vector<map<ll, int>> test = { + {}, // 1 + {{2, 1}}, + {{3, 1}}, + {{2, 2}}, + {{5, 1}}, + {{2, 1}, {3, 1}}, + {{2, 2}, {3, 1}}, + {{2, 1}, {3, 2}}, + {{2, 2}, {3, 2}}, + {{2, 62}}, + {{2, 18}, {5, 18}}, + {{352523, 1}, {352817, 1}}, + {{41, 1}, {71, 1}, {421, 1}, {811, 1}}, + {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}}, + {{500000003, 1}, {1999999973, 1}}, + {{65537, 2}}, + {{999665081, 1}, {999716071, 1}}, + {{550177, 1}, {1100353, 1}, {1650529, 1}}, + {{459397, 1}, {918793, 1}, {1378189, 1}}, + {{37, 1}, {109, 1}}, + {{31, 1}, {151, 1}}, + {{239, 1}, {1429, 1}}, + {{89, 1}, {1093, 1}}, + {{2, 3}, {15800133918749317, 1}}, + {{12251, 1}, {85751, 1}}, + {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}}, + {{5, 1}, {1927962474784631, 1}}, + {{197279, 1}, {1775503, 1}}, + {{3716371, 1}, {14865481, 1}}, + {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}}, + {{4880401, 1}, {9760801, 1}}, + {{2822159, 1}, {11288633, 1}}, + {{3290341, 1}, {6580681, 1}}, + {{611557, 1}, {1834669, 1}}, + {{9227, 1}, {894923, 1}, {968731, 1}}, + {{3, 4}, {13, 1}, {62633, 2}}, + {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}}, + {{332721269, 1}, {560937673, 1}}, + {{30702523, 1}, {122810089, 1}}, + {{24786439, 1}, {123932191, 1}}, + {{382500329, 1}, {1530001313, 1}}, + {{2, 4}, {5, 4}, {13, 1}, {30839, 2}}, + {{3, 1}, {385417, 1}, {7985344259, 1}}, + {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}}, + {{627838711, 1}, {1212379867, 1}}, + {{3, 5}, {5, 3}, {41, 2}, {157321, 2}}, + {{5, 2}, {13, 1}}, + {{3, 1}, {5, 5}}, + {{2, 1}, {73, 1}, {193, 1}}, + {{5, 2}, {13, 1}, {19, 1}, {73, 1}}, + {{2, 3}, {3, 1}, {407521, 1}}, + {{2, 1}, {3, 1}, {299210837, 1}}, + {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}}, + }; + + timer t; + for (auto expected : test) { + ll x = mul(expected); + t.start(); + auto got = factor(x); + t.stop(); + if (got != expected) { + cerr << "number: " << x << endl; + cerr << "got:" << endl; + for (auto [p, c] : got) cerr << p << "^" << c << endl; + cerr << "expected" << endl; + for (auto [p, c] : expected) cerr << p << "^" << c << endl; + cerr << FAIL; + } + } + if (t.time > 100) cerr << "too slow" << FAIL; + cerr << "stress tested: " << t.time << "ms" << endl; +} + +constexpr int N = 2'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll x = Random::integer<ll>(1e18 / 2, 1e18); + t.start(); + hash += factor(x).size(); + t.stop(); + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/shortModInv.cpp b/test/math/shortModInv.cpp new file mode 100644 index 0000000..26960bf --- /dev/null +++ b/test/math/shortModInv.cpp @@ -0,0 +1,39 @@ +#include "../util.h" +#include <math/shortModInv.cpp> + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 10'000'000; i++) { + ll n = Random::integer<ll>(2, 1'000'000'000); + ll x = 0; + do { + x = Random::integer<ll>(0, n); + } while (gcd(x, n) != 1); + ll y = multInv(x, n); + ll got = (x*y) % n; + if (got != 1) cerr << "got: " << got << ", expected: 1" << FAIL; + queries++; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll a = Random::integer<ll>(0, 1'000'000'000); + ll b = Random::integer<ll>(2, 1'000'000'000); + t.start(); + hash += multInv(a, b); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/simpson.cpp b/test/math/simpson.cpp new file mode 100644 index 0000000..d7cdba3 --- /dev/null +++ b/test/math/simpson.cpp @@ -0,0 +1,63 @@ +#include "../util.h" +std::function<double(double)> f; +constexpr double EPS = 1e-9; +#include <math/simpson.cpp> + +struct RandomPolynom { + vector<double> polynom; + RandomPolynom(int deg) : polynom(deg) { + for (auto& x : polynom) x = Random::real<double>(-100, 100); + } + double operator()(double x) const { + double res = 0; + double xx = 1; + for (double y : polynom ) { + res += xx * y; + xx *= x; + } + return res; + } + double area(double a, double b) const { + double res = 0; + double aa = a; + double bb = b; + ll d = 1; + for (double y : polynom) { + res += bb / d * y; + res -= aa / d * y; + aa *= a; + bb *= b; + d++; + } + return res; + } +}; + +void stress_test() { + timer t; + ll queries = 0; + for (int tries = 0; tries < 1'000; tries++) { + ll n = Random::integer<ll>(0, 6); + RandomPolynom poly(n); + f = poly; + for (ll i = 0; i < 200; i++) { + double l = Random::real<double>(-20, 20); + double r = Random::real<double>(-20, 20); + if (l > r) swap(l, r); + + t.start(); + double got = integrate(l, r); + t.stop(); + double expected = poly.area(l, r); + if (float_error(got, expected) > 1e-6) cerr << fixed << setprecision(20) << "got: " << got << ", expected: " << expected << FAIL; + queries++; + } + } + if (t.time > 5000) cerr << "too slow: " << t.time << FAIL; + cerr << "tested random queries: " << queries << " (" << t.time << "ms)" << endl; +} + +int main() { + stress_test(); +} + diff --git a/test/math/sqrtModCipolla.cpp b/test/math/sqrtModCipolla.cpp new file mode 100644 index 0000000..26d975b --- /dev/null +++ b/test/math/sqrtModCipolla.cpp @@ -0,0 +1,48 @@ +#include "../util.h" +#include <math/modPowIterativ.cpp> +#include <math/legendre.cpp> +mt19937 rng(123456789); +#include <math/sqrtModCipolla.cpp> + +void stress_test(ll range) { + ll work = 0; + for (ll i = 0; i < 10'000; i++) { + ll p = Random::prime<ll>(range); + for (ll j = 0; j < 100; j++) { + ll x = Random::integer<ll>(0, p); + if (legendre(x, p) < 0) continue; + + ll got = sqrtMod(x, p); + if (got < 0 || got >= p) cerr << "error: out of range" << FAIL; + if ((got * got) % p != x) cerr << "error: not root" << FAIL; + work++; + } + } + cerr << "stress tested: " << work << endl; +} + +constexpr int N = 200'000; +constexpr ll mod = 1'394'633'899; +void performance_test() { + timer t; + hash_t hash = 0; + for (int operations = 0; operations < N; operations++) { + ll x; + do { + x = Random::integer<ll>(0, mod); + } while (legendre(x, mod) >= 0); + t.start(); + hash += sqrtMod(x, mod); + t.stop(); + } + if (t.time > 750) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + + +int main() { + stress_test(1'000); + stress_test(1'000'000'000); + performance_test(); +} + diff --git a/test/math/transforms/andTransform.cpp b/test/math/transforms/andTransform.cpp new file mode 100644 index 0000000..fa029f6 --- /dev/null +++ b/test/math/transforms/andTransform.cpp @@ -0,0 +1,38 @@ +#include "../../util.h" +#include <math/transforms/andTransform.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, -1000, 1000); + auto got = expected; + fft(got, false); + fft(got, true); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 23; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + fft(a, true); + fft(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * a[i]; + for (ll i = 0; i < N; i++) hash += i * b[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/bitwiseTransforms.cpp b/test/math/transforms/bitwiseTransforms.cpp new file mode 100644 index 0000000..132740c --- /dev/null +++ b/test/math/transforms/bitwiseTransforms.cpp @@ -0,0 +1,38 @@ +#include "../../util.h" +#include <math/transforms/bitwiseTransforms.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, -1000, 1000); + auto got = expected; + bitwiseConv(got, false); + bitwiseConv(got, true); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 23; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + bitwiseConv(a, true); + bitwiseConv(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * a[i]; + for (ll i = 0; i < N; i++) hash += i * b[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/fft.cpp b/test/math/transforms/fft.cpp new file mode 100644 index 0000000..858676b --- /dev/null +++ b/test/math/transforms/fft.cpp @@ -0,0 +1,51 @@ +#include "../../util.h" +#include <math/transforms/fft.cpp> + +vector<cplx> to_cplx(const vector<ll>& in) { + vector<cplx> res(sz(in)); + for (int i = 0; i < sz(in); i++) res[i] = in[i]; + return res; +} + +vector<ll> from_cplx(const vector<cplx>& in) { + vector<ll> res(sz(in)); + for (int i = 0; i < sz(in); i++) res[i] = llround(real(in[i])); + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, -1000, 1000); + vector<cplx> tmp = to_cplx(expected); + fft(tmp, false); + fft(tmp, true); + auto got = from_cplx(tmp); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 21; +void performance_test() { + timer t; + auto a = to_cplx(Random::integers<ll>(N, -1000, 1000)); + auto b = to_cplx(Random::integers<ll>(N, -1000, 1000)); + t.start(); + fft(a, true); + fft(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * llround(real(a[i])); + for (ll i = 0; i < N; i++) hash += i * llround(real(b[i])); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/fftMul.cpp b/test/math/transforms/fftMul.cpp new file mode 100644 index 0000000..5933864 --- /dev/null +++ b/test/math/transforms/fftMul.cpp @@ -0,0 +1,62 @@ +#include "../../util.h" +#include <math/modPowIterativ.cpp> +#include <math/transforms/fft.cpp> +#pragma GCC diagnostic ignored "-Wnarrowing" +#include <math/transforms/fftMul.cpp> + +vector<ll> from_cplx(const vector<cplx>& in) { + vector<ll> res(sz(in)); + for (int i = 0; i < sz(in); i++) res[i] = llround(real(in[i])); + return res; +} + +vector<ll> naive(const vector<ll>& a, const vector<ll>& b) { + vector<ll> res; + for (ll i = 1;; i *= 2) { + if (sz(a) + sz(b) <= i) { + res.resize(i, 0); + break; + } + } + for (int i = 0; i < sz(a); i++) { + for (int j = 0; j < sz(b); j++) { + res[i+j] += a[i] * b[j]; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + int m = Random::integer<int>(1, 100); + auto a = Random::integers<ll>(n, -1000, 1000); + auto b = Random::integers<ll>(m, -1000, 1000); + auto expected = naive(a, b); + auto got = from_cplx(mul(a, b)); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 20; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + auto got = from_cplx(mul(a, b)); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * got[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/multiplyBitwise.cpp b/test/math/transforms/multiplyBitwise.cpp new file mode 100644 index 0000000..bc73290 --- /dev/null +++ b/test/math/transforms/multiplyBitwise.cpp @@ -0,0 +1,55 @@ +#include "../../util.h" +#include <math/modPowIterativ.cpp> +#include <math/transforms/bitwiseTransforms.cpp> +#include <math/transforms/multiplyBitwise.cpp> + +vector<ll> naive(const vector<ll>& a, const vector<ll>& b) { + vector<ll> res; + for (ll i = 1;; i *= 2) { + if (sz(a) <= i && sz(b) <= i) { + res.resize(i, 0); + break; + } + } + for (int i = 0; i < sz(a); i++) { + for (int j = 0; j < sz(b); j++) { + res[i&j] += a[i] * b[j]; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + int m = Random::integer<int>(1, 100); + auto a = Random::integers<ll>(n, -1000, 1000); + auto b = Random::integers<ll>(m, -1000, 1000); + auto expected = naive(a, b); + auto got = mul(a, b); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 22; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + auto got = mul(a, b); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * got[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/multiplyFFT.cpp b/test/math/transforms/multiplyFFT.cpp new file mode 100644 index 0000000..782be1b --- /dev/null +++ b/test/math/transforms/multiplyFFT.cpp @@ -0,0 +1,55 @@ +#include "../../util.h" +#include <math/modPowIterativ.cpp> +#include <math/transforms/fft.cpp> +#include <math/transforms/multiplyFFT.cpp> + +vector<ll> naive(const vector<ll>& a, const vector<ll>& b) { + vector<ll> res; + for (ll i = 1;; i *= 2) { + if (sz(a) + sz(b) <= i) { + res.resize(i, 0); + break; + } + } + for (int i = 0; i < sz(a); i++) { + for (int j = 0; j < sz(b); j++) { + res[i+j] += a[i] * b[j]; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + int m = Random::integer<int>(1, 100); + auto a = Random::integers<ll>(n, -1000, 1000); + auto b = Random::integers<ll>(m, -1000, 1000); + auto expected = naive(a, b); + auto got = mul(a, b); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 19; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + auto got = mul(a, b); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * got[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/multiplyNTT.cpp b/test/math/transforms/multiplyNTT.cpp new file mode 100644 index 0000000..70fc137 --- /dev/null +++ b/test/math/transforms/multiplyNTT.cpp @@ -0,0 +1,56 @@ +#include "../../util.h" +#include <math/modPowIterativ.cpp> +#include <math/transforms/ntt.cpp> +#include <math/transforms/multiplyNTT.cpp> + +vector<ll> naive(const vector<ll>& a, const vector<ll>& b) { + vector<ll> res; + for (ll i = 1;; i *= 2) { + if (sz(a) + sz(b) <= i) { + res.resize(i, 0); + break; + } + } + for (int i = 0; i < sz(a); i++) { + for (int j = 0; j < sz(b); j++) { + res[i+j] += a[i] * b[j]; + res[i+j] %= mod; + } + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + int m = Random::integer<int>(1, 100); + auto a = Random::integers<ll>(n, 0, mod); + auto b = Random::integers<ll>(m, 0, mod); + auto expected = naive(a, b); + auto got = mul(a, b); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 20; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, 0, mod); + vector<ll> b = Random::integers<ll>(N, 0, mod); + t.start(); + auto got = mul(a, b); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * got[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/ntt.cpp b/test/math/transforms/ntt.cpp new file mode 100644 index 0000000..cd32073 --- /dev/null +++ b/test/math/transforms/ntt.cpp @@ -0,0 +1,39 @@ +#include "../../util.h" +#include <math/modPowIterativ.cpp> +#include <math/transforms/ntt.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, 0, mod); + auto got = expected; + ntt(got, false); + ntt(got, true); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 22; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, 0, mod); + vector<ll> b = Random::integers<ll>(N, 0, mod); + t.start(); + ntt(a, true); + ntt(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * a[i]; + for (ll i = 0; i < N; i++) hash += i * b[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/orTransform.cpp b/test/math/transforms/orTransform.cpp new file mode 100644 index 0000000..0ec9155 --- /dev/null +++ b/test/math/transforms/orTransform.cpp @@ -0,0 +1,38 @@ +#include "../../util.h" +#include <math/transforms/orTransform.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, -1000, 1000); + auto got = expected; + fft(got, false); + fft(got, true); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 23; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + fft(a, true); + fft(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * a[i]; + for (ll i = 0; i < N; i++) hash += i * b[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/math/transforms/xorTransform.cpp b/test/math/transforms/xorTransform.cpp new file mode 100644 index 0000000..17b0f6f --- /dev/null +++ b/test/math/transforms/xorTransform.cpp @@ -0,0 +1,38 @@ +#include "../../util.h" +#include <math/transforms/xorTransform.cpp> + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = 1ll << Random::integer<int>(0, 10); + auto expected = Random::integers<ll>(n, -1000, 1000); + auto got = expected; + fft(got, false); + fft(got, true); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested queries: " << queries << endl; +} + +constexpr int N = 1ll << 23; +void performance_test() { + timer t; + vector<ll> a = Random::integers<ll>(N, -1000, 1000); + vector<ll> b = Random::integers<ll>(N, -1000, 1000); + t.start(); + fft(a, true); + fft(b, false); + t.stop(); + hash_t hash = 0; + for (ll i = 0; i < N; i++) hash += i * a[i]; + for (ll i = 0; i < N; i++) hash += i * b[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/compiletime.cpp b/test/other/compiletime.cpp new file mode 100644 index 0000000..591669d --- /dev/null +++ b/test/other/compiletime.cpp @@ -0,0 +1,2 @@ +#include <other/compiletime.cpp> +int main() {}
\ No newline at end of file diff --git a/test/other/divideAndConquer.cpp b/test/other/divideAndConquer.cpp new file mode 100644 index 0000000..a6fda9d --- /dev/null +++ b/test/other/divideAndConquer.cpp @@ -0,0 +1,103 @@ +#include "../util.h" +constexpr ll inf = LL::INF; +#include <other/divideAndConquer.cpp> + +vector<vector<ll>> gen(int n) { + vector<vector<ll>> res(n, vector<ll>(n)); + ll mi = 0; + for (ll a = n-1; a >= 0; a--) { + for (ll c = n-1; c >= a; c--) { + for (ll b = a; b <= c; b++) { + for (ll d = c; d < n; d++) { + res[a][c] = min(res[a][c], res[a][d] + res[b][c] - res[b][d]); + } + } + res[a][c] -= Random::integer<ll>(0, 1000); + mi = min(mi, res[a][c]); + } + } + for (auto& v : res) for (auto& x : v) x -= mi; + + for (ll a = 0; a < n; a++) { + for (ll b = a; b < n; b++) { + for (ll c = b; c < n; c++) { + for (ll d = c; d < n; d++) { + if (res[a][d] < 0 || res[a][d] + res[b][c] < res[a][c] + res[b][d]) { + cerr << "invalid C array!" << FAIL; + } + } + } + } + } + return res; +} + +vector<vector<ll>> genQuick(int n) { + vector<vector<ll>> res(n, vector<ll>(n)); + for (ll a = n-1; a >= 0; a--) { + for (ll c = n-1; c >= a; c--) { + res[a][c] = (c-a) * (c - a) + Random::integer<ll>(0, 2); + } + } + return res; +} + +/*ll naive(int n, int m) { + vector<vector<ll>> state(m+1, vector<ll>(n+1, inf)); + state[0][0] = 0; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + for (int k = 1; k <= j; k++) { + state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]); + } + } + } + return state[m][n]; +}*/ + +vector<ll> naive(int n) { + vector<vector<ll>> state(n+1, vector<ll>(n+1, inf)); + state[0][0] = 0; + vector<ll> res(n+1, inf); + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + for (int k = 1; k <= j; k++) { + state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]); + } + } + res[i] = state[i][n]; + } + return res; +} + +void stress_test() { + ll tests = 0; + for (ll i = 0; i < 1000; i++) { + auto n = Random::integer(10, 20); + C = gen(n); + auto expected = naive(n); + for (ll m = 1; m <= n; m++) { + auto got = calc(n, m); + if (got != expected[m]) cerr << "got: " << got << ", expected: " << expected[m] << FAIL; + tests++; + } + } + cerr << "tested random queries: " << tests << endl; +} + +constexpr int N = 10'000; +void performance_test() { + timer t; + C = genQuick(N); + t.start(); + auto hash = calc(N, 32); + t.stop(); + if (t.time > 50) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/fastIO.cpp b/test/other/fastIO.cpp new file mode 100644 index 0000000..765ddba --- /dev/null +++ b/test/other/fastIO.cpp @@ -0,0 +1,32 @@ +#include "../util.h" +#include <other/fastIO.cpp> + +int main() { + if (freopen("other/fastIO.in", "r", stdin) == nullptr) cerr << "fastIO.in not found" << FAIL; + vector<int> got(5); + vector<int> expected = {4, 7, 3, 6, 9}; + for (int& x : got) fastscan(x); + if (got != expected) cerr << "failed fastscan" << FAIL; + + if (freopen("other/fastIO.out", "w", stdout) == nullptr) cerr << "fastIO.out not writebale" << FAIL; + fastprint(0); + putchar('\n'); + fastprint(-1); + putchar(' '); + fastprint(-8321648); + putchar(' '); + fastprint(1); + putchar(' '); + fastprint(42387); + putchar('\n'); + fclose(stdout); + + stringstream buffer; + { + ifstream tmp("other/fastIO.out"); + buffer << tmp.rdbuf(); + } + if (buffer.str() != "0\n-1 -8321648 1 42387\n") cerr << "failed fastprint" << FAIL; + cerr << "done" << endl; +} + diff --git a/test/other/fastIO.in b/test/other/fastIO.in new file mode 100644 index 0000000..45594a4 --- /dev/null +++ b/test/other/fastIO.in @@ -0,0 +1,2 @@ +4 7 +3 6 9
\ No newline at end of file diff --git a/test/other/josephus2.cpp b/test/other/josephus2.cpp new file mode 100644 index 0000000..d28fe0d --- /dev/null +++ b/test/other/josephus2.cpp @@ -0,0 +1,42 @@ +#include "../util.h" +#include <other/josephus2.cpp> + +template<ll O> +ll naive(ll n, ll k) { + vector<ll> state(n); + iota(all(state), O); + for (ll i = k-1; state.size() > 1; i = (i + k - 1) % sz(state)) { + state.erase(state.begin() + i); + } + return state[0]; +} + +void stress_test() { + ll tests = 0; + for (ll i = 1; i < 2'000; i++) { + auto got = rotateLeft(i); + auto expected = naive<1>(i, 2); + if (got != expected) cerr << "error: " << i << FAIL; + tests++; + } + cerr << "tested queries: " << tests << endl; +} + +constexpr int N = 1'000'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + t.start(); + for (ll i = 0; i < N; i++) { + hash += rotateLeft(1'000'000'000'000'000'000ll + i); + } + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/josephusK.cpp b/test/other/josephusK.cpp new file mode 100644 index 0000000..e837640 --- /dev/null +++ b/test/other/josephusK.cpp @@ -0,0 +1,43 @@ +#include "../util.h" +#include <other/josephus2.cpp> +#include <other/josephusK.cpp> + +template<ll O> +ll naive(ll n, ll k) { + vector<ll> state(n); + iota(all(state), O); + for (ll i = k-1; state.size() > 1; i = (i + k - 1) % sz(state)) { + state.erase(state.begin() + i); + } + return state[0]; +} + +void stress_test() { + ll tests = 0; + for (ll i = 1; i < 500; i++) { + for (ll j = 1; j <= i; j++) { + auto got = josephus(i, j); + auto expected = naive<0>(i, j); + if (got != expected) cerr << "error: " << i << FAIL; + tests++; + } + } + cerr << "tested queries: " << tests << endl; +} + +constexpr int N = 10'000'000; +void performance_test() { + timer t; + hash_t hash = 0; + t.start(); + hash += josephus(N, N/2); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/knuth.cpp b/test/other/knuth.cpp new file mode 100644 index 0000000..d469ceb --- /dev/null +++ b/test/other/knuth.cpp @@ -0,0 +1,103 @@ +#include "../util.h" +constexpr ll inf = LL::INF; +#include <other/knuth.cpp> + +vector<vector<ll>> gen(int n) { + vector<vector<ll>> res(n, vector<ll>(n)); + ll mi = 0; + for (ll a = n-1; a >= 0; a--) { + for (ll c = n-1; c >= a; c--) { + for (ll b = a; b <= c; b++) { + for (ll d = c; d < n; d++) { + res[a][c] = min(res[a][c], res[a][d] + res[b][c] - res[b][d]); + } + } + res[a][c] -= Random::integer<ll>(0, 1000); + mi = min(mi, res[a][c]); + } + } + for (auto& v : res) for (auto& x : v) x -= mi; + + for (ll a = 0; a < n; a++) { + for (ll b = a; b < n; b++) { + for (ll c = b; c < n; c++) { + for (ll d = c; d < n; d++) { + if (res[a][d] < 0 || res[a][d] + res[b][c] < res[a][c] + res[b][d]) { + cerr << "invalid C array!" << FAIL; + } + } + } + } + } + return res; +} + +vector<vector<ll>> genQuick(int n) { + vector<vector<ll>> res(n, vector<ll>(n)); + for (ll a = n-1; a >= 0; a--) { + for (ll c = n-1; c >= a; c--) { + res[a][c] = (c-a) * (c - a) + Random::integer<ll>(0, 2); + } + } + return res; +} + +/*ll naive(int n, int m, const vector<vector<ll>>& C) { + vector<vector<ll>> state(m+1, vector<ll>(n+1, inf)); + state[0][0] = 0; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + for (int k = 1; k <= j; k++) { + state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]); + } + } + } + return state[m][n]; +}*/ + +vector<ll> naive(int n, const vector<vector<ll>>& C) { + vector<vector<ll>> state(n+1, vector<ll>(n+1, inf)); + state[0][0] = 0; + vector<ll> res(n+1, inf); + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + for (int k = 1; k <= j; k++) { + state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]); + } + } + res[i] = state[i][n]; + } + return res; +} + +void stress_test() { + ll tests = 0; + for (ll i = 0; i < 1000; i++) { + auto n = Random::integer(10, 20); + auto C = gen(n); + auto expected = naive(n, C); + for (ll m = 1; m <= n; m++) { + auto got = calc(n, m, C); + if (got != expected[m]) cerr << "got: " << got << ", expected: " << expected[m] << FAIL; + tests++; + } + } + cerr << "tested random queries: " << tests << endl; +} + +constexpr int N = 5000; +void performance_test() { + timer t; + auto C = genQuick(N); + t.start(); + auto hash = calc(N, N/2, C); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/sos.cpp b/test/other/sos.cpp new file mode 100644 index 0000000..f3a6109 --- /dev/null +++ b/test/other/sos.cpp @@ -0,0 +1,50 @@ +#include "../util.h" + +vector<ll> sos(const vector<ll>& in) { + #include <other/sos.cpp> + return res; +} + +vector<ll> naive(const vector<ll>& in) { + vector<ll> res(sz(in)); + for (ll i = 0; i < sz(in); i++) { + for (ll j = 0; j <= i; j++) { + if ((i | j) == i) { + res[i] += in[j]; + } + } + } + return res; +} + +void stress_test() { + ll tests = 0; + for (ll i = 0; i < 1000; i++) { + int n = Random::integer<int>(1, 100); + auto in = Random::integers<ll>(n, -1000, 1000); + auto got = sos(in); + auto expected = naive(in); + if (got != expected) cerr << "error: " << i << FAIL; + tests += n; + } + cerr << "tested random queries: " << tests << endl; +} + +constexpr int N = 10'000'000; +void performance_test() { + timer t; + auto in = Random::integers<ll>(N, -1000, 1000); + t.start(); + auto res = sos(in); + t.stop(); + hash_t hash = 0; + for (ll x : res) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} + diff --git a/test/other/split.cpp b/test/other/split.cpp new file mode 100644 index 0000000..e0f5ee1 --- /dev/null +++ b/test/other/split.cpp @@ -0,0 +1,24 @@ +#include "../util.h" +#include <other/split.cpp> + +vector<string> split2(string_view s, string_view delim) { + vector<string> res; + while (!s.empty()) { + auto end = s.find_first_of(delim); + if (end != 0) res.emplace_back(s.substr(0, end)); + if (end == string_view::npos) break; + s.remove_prefix(end + 1); + } + return res; +} + +int main() { + auto in = "+" + Random::string(100, "abcdef+-*") + "-"; + + auto expected = split2(in, "+-*"); + auto got = split(in, "+-*"); + + if (got != expected) cerr << "error" << FAIL; + cerr << "done" << endl; +} + diff --git a/test/string/ahoCorasick.cpp b/test/string/ahoCorasick.cpp new file mode 100644 index 0000000..c3361d6 --- /dev/null +++ b/test/string/ahoCorasick.cpp @@ -0,0 +1,76 @@ +#include "../util.h" +#include <string/ahoCorasick.cpp> + +vector<ll> naive(string s, vector<string> patterns) { + vector<ll> ans(patterns.size()); + for (int k = 0; k < (int)patterns.size(); k++) { + string pattern = patterns[k]; + for (int i = 0; i + pattern.size() <= s.size(); i++) { + if (s.substr(i, pattern.size()) == pattern) ans[k]++; + } + } + return ans; +} + +vector<ll> normal(string s, vector<string> patterns) { + AhoCorasick aho; + vector<int> ind(patterns.size()); + for (int i = 0; i < (int)patterns.size(); i++) { + ind[i] = aho.addString(patterns[i]); + } + aho.buildGraph(); + + int v = 0; + for (char c : s) v = aho.go(v, c - OFFSET), aho.dp[v]++; + aho.dfs(); + vector<ll> ans(patterns.size()); + for (int i = 0; i < (int)patterns.size(); i++) { + ans[i] = aho.dp[ind[i]]; + } + return ans; +} + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 100; i++) { + int n = Random::integer(1, 100); + string s = Random::string(n, "abc"); + int m = Random::integer(1, 100); + vector<string> patterns(m); + for (string& e : patterns) { + int k = Random::integer(1, 100); + e = Random::string(k, "abc"); + } + + auto got = normal(s, patterns); + auto expected = naive(s, patterns); + if (got != expected) cerr << "Wrong Answer" << FAIL; + queries++; + } + cerr << "Tested random queries: " << queries << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + string s = string(N, 'a') + Random::string(N, "ab"); + vector<string> patterns = {"a"}; + for (int sm = 1; sm < N; sm += patterns.back().size()) { + patterns.emplace_back(patterns.back().size()+1, 'a'); + } + for (int i = 0; i < 100; i++) { + patterns.emplace_back(Random::string(N/100, "ab")); + } + + t.start(); + hash_t hash = normal(s, patterns)[0]; + t.stop(); + + if (t.time > 500) cerr << "Too slow: " << t.time << FAIL; + cerr << "Tested performance: " << t.time << "ms (hash: hash " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/deBruijn.cpp b/test/string/deBruijn.cpp new file mode 100644 index 0000000..6b3fea4 --- /dev/null +++ b/test/string/deBruijn.cpp @@ -0,0 +1,43 @@ +#include "../util.h" +#include <string/lyndon.cpp> +#include <string/deBruijn.cpp> + +bool isDeBruijn(string s, int n, int k) { + ll expected = 1; + for (ll i = 0; i < n; i++) expected *= k; + if (expected != sz(s)) return false; + s += s; + set<string_view> seen; + for (ll i = 0; 2*i < sz(s); i++) { + seen.insert(string_view(s).substr(i, n)); + } + return sz(seen) == expected; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 1000; i++) { + int n = Random::integer<int>(1, 9); + auto [l, r] = Random::pair<char>('b', 'f'); + auto got = deBruijn(n, l, r); + if (!isDeBruijn(got, n, r - l + 1)) cerr << "error" << FAIL; + queries += sz(got); + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 26; +void performance_test() { + timer t; + t.start(); + auto res = deBruijn(N, '0', '1'); + t.stop(); + hash_t hash = sz(res); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/duval.cpp b/test/string/duval.cpp new file mode 100644 index 0000000..58b4a44 --- /dev/null +++ b/test/string/duval.cpp @@ -0,0 +1,85 @@ +#include "../util.h" +#pragma GCC diagnostic ignored "-Wreturn-type" +#include <string/duval.cpp> + +constexpr int N = 20'000'000; + +bool isLyndon(string_view s) { + string t = string(s) + string(s); + for (ll i = 1; i < sz(s); i++) { + if (s >= t.substr(i, sz(s))) return false; + } + return !s.empty(); +} + +void stress_test_duval() { + ll queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + auto s = Random::string(n, "abc"); + vector<pair<int, int>> got = duval(s); + if (got.empty()) cerr << "error: a" << FAIL; + if (got.front().first != 0) cerr << "error: b" << FAIL; + if (got.back().second != n) cerr << "error: c" << FAIL; + for (int j = 1; j < sz(got); j++) { + if (got[j - 1].second != got[j].first) cerr << "error: d" << FAIL; + } + for (auto [l, r] : got) { + if (!isLyndon(string_view(s).substr(l, r-l))) cerr << "error: e" << FAIL; + } + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test_duval() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + auto got = duval(s); + t.stop(); + hash_t hash = 0; + for (auto [l, r] : got) hash += l + r; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int naive(string s) { + ll n = sz(s); + s += s; + int res = 0; + for (int i = 0; i < n; i++) { + if (string_view(s).substr(i, n) <= string_view(s).substr(res, n)) res = i; + } + return res; +} + +void stress_test_minrotation() { + ll queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + auto s = Random::string(n, "abc"); + int got = minrotation(s); + auto expected = naive(s); + if (got != expected) cerr << s << ": got: " << got << ", expected: " << expected << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +void performance_test_minrotation() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + hash_t hash = minrotation(s); + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test_duval(); + performance_test_duval(); + stress_test_minrotation(); + performance_test_minrotation(); +} diff --git a/test/string/kmp.cpp b/test/string/kmp.cpp new file mode 100644 index 0000000..9c9c924 --- /dev/null +++ b/test/string/kmp.cpp @@ -0,0 +1,85 @@ +#include "../util.h" +#include <string/kmp.cpp> + +vector<int> naive(string_view s) { + vector<int> res(sz(s) + 1, -1); + for (int i = 0; i < sz(s); i++) { + for (int j = 0; j <= i; j++) + if (s.substr(0, j) == s.substr(i-j+1, j)) + res[i+1] = j; + } + return res; +} + +void stress_test_preprocessing() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 15); + auto s = Random::string(n, "abc"); + auto got = kmpPreprocessing(s); + auto expected = naive(s); + if (got != expected) cerr << " error" << FAIL; + queries += n; + } + cerr << " tested random queries: " << queries << endl; +} + +constexpr int N = 10'000'000; +void performance_test_preprocessing() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + auto res = kmpPreprocessing(s); + t.stop(); + hash_t hash = 0; + for (int x : res) hash += x; + if (t.time > 500) cerr << " too slow: " << t.time << FAIL; + cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +vector<int> naive(string_view s, string_view sub) { + vector<int> res; + auto pos = s.find(sub); + while (pos != string_view::npos) { + res.push_back(pos); + pos = s.find(sub, pos + 1); + } + return res; +} + +void stress_test_kmp() { + ll queries = 0; + auto a = Random::string(10'000, "abc"); + for (int tries = 0; tries < 10'000; tries++) { + int n = Random::integer<int>(1, 10); + auto b = Random::string(n, "abc"); + auto got = kmpSearch(a, b); + auto expected = naive(a, b); + if (got != expected) cerr << " error" << FAIL; + queries += got.size(); + } + cerr << " tested random queries: " << queries << endl; +} + +void performance_test_kmp() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + auto sub1 = Random::string(N/2, "a"); + auto sub2 = Random::string(N/2, "ab"); + hash_t hash = 0; + t.start(); + hash += kmpSearch(s, sub1).size(); + hash += kmpSearch(s, sub2).size(); + t.stop(); + if (t.time > 500) cerr << " too slow: " << t.time << FAIL; + cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + cerr << "preprocessing:" << endl; + stress_test_preprocessing(); + performance_test_preprocessing(); + cerr << "kmp:" << endl; + stress_test_kmp(); + performance_test_kmp(); +} diff --git a/test/string/longestCommonSubsequence.cpp b/test/string/longestCommonSubsequence.cpp new file mode 100644 index 0000000..6d7a6c5 --- /dev/null +++ b/test/string/longestCommonSubsequence.cpp @@ -0,0 +1,55 @@ +#include "../util.h" +#include <string/longestCommonSubsequence.cpp> + +bool isSubstr(string_view s, string_view sub) { + int i = 0; + for (char c : s) { + if (i < sz(sub) && c == sub[i]) i++; + } + return i >= sz(sub); +} + +string naive(string_view s, string_view t) { + string res = ""; + for (ll i = 1; i < (1ll << sz(s)); i++) { + string tmp; + for (ll j = 0; j < sz(s); j++) { + if (((i >> j) & 1) != 0) tmp.push_back(s[j]); + } + if (sz(tmp) >= sz(res) && isSubstr(t, tmp)) res = tmp; + } + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 12); + int m = Random::integer<int>(1, 12); + auto s = Random::string(n, "abc"); + auto t = Random::string(m, "abc"); + auto got = lcss(s, t); + auto expected = naive(s, t); + if (got != expected) cerr << s << ", " << t << ", got: " << got << ", expected: " << expected << FAIL; + queries += n + m; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 2'000; +void performance_test() { + timer t; + auto a = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + auto b = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + auto res = lcss(a, b); + t.stop(); + hash_t hash = sz(res); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/lyndon.cpp b/test/string/lyndon.cpp new file mode 100644 index 0000000..ecf2dad --- /dev/null +++ b/test/string/lyndon.cpp @@ -0,0 +1,61 @@ +#include "../util.h" +#include <string/lyndon.cpp> + +bool isLyndon(string_view s) { + string t = string(s) + string(s); + for (ll i = 1; i < sz(s); i++) { + if (s >= t.substr(i, sz(s))) return false; + } + return !s.empty(); +} + +vector<string> naive(ll n, char mi, char ma) { + vector<string> res; + auto dfs = [&](auto&& self, string pref)->void{ + if (sz(pref) <= n && isLyndon(pref)) res.push_back(pref); + if (sz(pref) >= n) return; + for (char c = mi; c <= ma; c++) { + self(self, pref + c); + } + }; + dfs(dfs, ""); + return res; +} + +vector<string> fast(ll n, char mi, char ma) { + vector<string> res; + string tmp(1, mi); + do { + res.push_back(tmp); + } while (next(tmp, n, mi, ma)); + return res; +} + +void stress_test() { + ll queries = 0; + for (ll i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 6); + auto [l, r] = Random::pair<char>('a', 'f'); + auto got = fast(n, l, r); + auto expected = naive(n, l, r); + if (got != expected) cerr << "error" << FAIL; + queries += sz(expected); + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 9; +void performance_test() { + timer t; + t.start(); + auto res = fast(N, 'a', 'f'); + t.stop(); + hash_t hash = sz(res); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/manacher.cpp b/test/string/manacher.cpp new file mode 100644 index 0000000..503d181 --- /dev/null +++ b/test/string/manacher.cpp @@ -0,0 +1,49 @@ +#include "../util.h" +#include <string/manacher.cpp> + +vector<int> naive(string_view s) { + vector<int> res(2 * sz(s) + 1); + for (int i = 0; i < sz(s); i++) { //odd palindromes + int j = 2*i+1; + while (i+res[j] < sz(s) && i-res[j] >= 0 && s[i-res[j]] == s[i+res[j]]) res[j]++; + res[j]*=2; + res[j]--; + } + for (int i = 0; i <= sz(s); i++) { //even palindromes + int j = 2*i; + while (i+res[j] < sz(s) && i-res[j]-1 >= 0 && s[i-res[j]-1] == s[i+res[j]]) res[j]++; + res[j] *= 2; + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 100); + auto s = Random::string(n, "abc"); + vector<int> got = manacher(s); + vector<int> expected = naive(s); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 5'000'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + auto got = manacher(s); + t.stop(); + hash_t hash = 0; + for (int x : got) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/rollingHash.cpp b/test/string/rollingHash.cpp new file mode 100644 index 0000000..0491bc0 --- /dev/null +++ b/test/string/rollingHash.cpp @@ -0,0 +1,92 @@ +#include "../util.h" +#include <string/rollingHash.cpp> + +string thueMorse(ll n) { + string res = "a"; + while (sz(res) < n) { + string tmp = res; + for (char& c : tmp) c ^= 1; + res += tmp; + } + return res; +} + +auto getHash(const string& s) { + return Hash(s)(0, sz(s)); +} + +void testThueMorse() { + set<decltype(getHash(""))> got; + set<string> expected; + string s = thueMorse(1000); + Hash h(s); + for (int l = 0; l < sz(s); l++) { + for (int r = l + 1; r <= sz(s); r++) { + got.insert(h(l, r)); + expected.insert(s.substr(l, r - l)); + } + } + if (sz(got) != sz(expected)) cerr << "error: thueMorse" << FAIL; + cerr << "thueMorse: ok" << endl; +} + +void testTiny() { + if (getHash("aa") == getHash("a")) cerr << "error: tiny" << FAIL; + if (getHash("00") == getHash("0")) cerr << "error: tiny" << FAIL; + if (getHash("AA") == getHash("A")) cerr << "error: tiny" << FAIL; + cerr << "tiny: ok" << endl; +} + +void testSmall() { + set<decltype(getHash(""))> got; + ll expected = 0; + auto dfs = [&](auto&& self, string pref)->void { + expected++; + got.insert(getHash(pref)); + if(sz(pref) >= 5) return; + for (char c = 'a'; c <= 'z'; c++) { + self(self, pref + c); + } + }; + dfs(dfs, ""); + if (sz(got) != expected) cerr << "error: small" << FAIL; + cerr << "small: ok" << endl; +} + +void stress_test() { + set<decltype(getHash(""))> got; + set<string> expected; + string s = Random::string(1000, "abc"); + Hash h(s); + for (int l = 0; l < sz(s); l++) { + for (int r = l + 1; r <= sz(s); r++) { + got.insert(h(l, r)); + expected.insert(s.substr(l, r - l)); + } + } + if (sz(got) != sz(expected)) cerr << "error: stress test" << FAIL; + cerr << "stress test: ok" << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + hash_t hash = 0; + t.start(); + Hash h(s); + for (ll i = 0; i < N; i++) { + hash += h(i, i + 2*N); + } + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + testThueMorse(); + testTiny(); + testSmall(); + stress_test(); + performance_test(); +} diff --git a/test/string/rollingHashCf.cpp b/test/string/rollingHashCf.cpp new file mode 100644 index 0000000..79003de --- /dev/null +++ b/test/string/rollingHashCf.cpp @@ -0,0 +1,94 @@ +#include "../util.h" +#include <string/rollingHashCf.cpp> + +constexpr ll RandomQ = 318LL << 53; + +string thueMorse(ll n) { + string res = "a"; + while (sz(res) < n) { + string tmp = res; + for (char& c : tmp) c ^= 1; + res += tmp; + } + return res; +} + +auto getHash(const string& s) { + return Hash(s, RandomQ)(0, sz(s)); +} + +void testThueMorse() { + set<decltype(getHash(""))> got; + set<string> expected; + string s = thueMorse(1000); + Hash h(s, RandomQ); + for (int l = 0; l < sz(s); l++) { + for (int r = l + 1; r <= sz(s); r++) { + got.insert(h(l, r)); + expected.insert(s.substr(l, r - l)); + } + } + if (sz(got) != sz(expected)) cerr << "error: thueMorse" << FAIL; + cerr << "thueMorse: ok" << endl; +} + +void testTiny() { + if (getHash("aa") == getHash("a")) cerr << "error: tiny" << FAIL; + if (getHash("00") == getHash("0")) cerr << "error: tiny" << FAIL; + if (getHash("AA") == getHash("A")) cerr << "error: tiny" << FAIL; + cerr << "tiny: ok" << endl; +} + +void testSmall() { + set<decltype(getHash(""))> got; + ll expected = 0; + auto dfs = [&](auto&& self, string pref)->void { + expected++; + got.insert(getHash(pref)); + if(sz(pref) >= 5) return; + for (char c = 'a'; c <= 'z'; c++) { + self(self, pref + c); + } + }; + dfs(dfs, ""); + if (sz(got) != expected) cerr << "error: small" << FAIL; + cerr << "small: ok" << endl; +} + +void stress_test() { + set<decltype(getHash(""))> got; + set<string> expected; + string s = Random::string(1000, "abc"); + Hash h(s, RandomQ); + for (int l = 0; l < sz(s); l++) { + for (int r = l + 1; r <= sz(s); r++) { + got.insert(h(l, r)); + expected.insert(s.substr(l, r - l)); + } + } + if (sz(got) != sz(expected)) cerr << "error: stress test" << FAIL; + cerr << "stress test: ok" << endl; +} + +constexpr int N = 1'000'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + hash_t hash = 0; + t.start(); + Hash h(s, RandomQ); + for (ll i = 0; i < N; i++) { + hash += h(i, i + 2*N); + } + t.stop(); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + testThueMorse(); + testTiny(); + testSmall(); + stress_test(); + performance_test(); +} diff --git a/test/string/suffixArray.cpp b/test/string/suffixArray.cpp new file mode 100644 index 0000000..4945d8e --- /dev/null +++ b/test/string/suffixArray.cpp @@ -0,0 +1,61 @@ +#include "../util.h" +#include <string/suffixArray.cpp> + +vector<int> naive(string_view s) { + vector<int> SA(sz(s)); + iota(all(SA), 0); + sort(all(SA), [s](int a, int b){ + return s.substr(a) < s.substr(b); + }); + return SA; +} + +int lcp(string_view s, int x, int y) { + int res = 0; + while (x + res < sz(s) && y + res < sz(s) && s[x + res] == s[y + res]) res++; + return res; +} + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 100; i++) { + int n = Random::integer<int>(1, 100); + auto s = Random::string(n, "abc"); + SuffixArray sa(s); + vector<int> got = sa.SA; + vector<int> expected = naive(s); + vector<int> SA(n); + if (got != expected) cerr << "error: SA" << FAIL; + got = sa.LCP; + swap(SA, expected); + for (int x = 0; x < n; x++) { + for (int y = 0; y < n; y++) { + int gotLCP = sa.lcp(x, y); + int expectedLCP = lcp(s, x, y); + if (gotLCP != expectedLCP) cerr << "error: lcp" << FAIL; + } + if (x > 0) expected[x] = lcp(s, SA[x-1], SA[x]); + } + if (got != expected) cerr << "error: LCP" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 200'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + SuffixArray sa(s); + t.stop(); + hash_t hash = 0; + for (int i = 0; i < sz(sa.SA); i++) hash += i*sa.SA[i]; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/suffixAutomaton.cpp b/test/string/suffixAutomaton.cpp new file mode 100644 index 0000000..c2ff511 --- /dev/null +++ b/test/string/suffixAutomaton.cpp @@ -0,0 +1,62 @@ +#include "../util.h" +#include <string/suffixAutomaton.cpp> + +pair<int, int> naive(string_view s, string_view t) { + int pos = 0; + int len = 0; + for (int j = 0; j < sz(t); j++) { + for (int i = 0; i < sz(s); i++) { + int cur = 0; + while (i+cur < sz(s) && j+cur < sz(t) && s[i+cur] == t[j+cur]) cur++; + if (cur > len) { + pos = j; + len = cur; + } + } + } + return {pos, len}; +} + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 1000; i++) { + int n = Random::integer<int>(1, 100); + auto s = Random::string(n, "abc"); + SuffixAutomaton sa(s); + for (int j = 0; j < 1000; j++) { + int m = Random::integer<int>(1, 100); + auto t = Random::string(m, "abc"); + auto got = sa.longestCommonSubstring(t); + auto expected = naive(s, t); + if (got != expected) cerr << "error" << FAIL; + queries += m; + } + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 500'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyz"); + t.start(); + SuffixAutomaton sa(s); + t.stop(); + hash_t hash = 0; + for (ll c = 0; c < sz(s);) { + int m = Random::integer<int>(1, 1000); + s = Random::string(m, "abc"); + t.start(); + auto [p, l] = sa.longestCommonSubstring(s); + t.stop(); + hash += l + p; + c += m; + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/suffixTree.cpp b/test/string/suffixTree.cpp new file mode 100644 index 0000000..c0d79e4 --- /dev/null +++ b/test/string/suffixTree.cpp @@ -0,0 +1,50 @@ +#include "../util.h" +#include <string/suffixTree.cpp> + +vector<string> naive(string_view s) { + vector<string> res(sz(s)); + for (ll i = 0; i < sz(s); i++) { + res[i] = s.substr(i); + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int i = 0; i < 10'000; i++) { + int n = Random::integer<int>(1, 15); + auto s = Random::string(n, "abc") + "#"; + SuffixTree st(s); + vector<string> got(n + 1); + auto dfs = [&](auto&& self, string pref, ll node) -> void { + auto& [l, r, _, next] = st.tree[node]; + if (l >= 0) pref += s.substr(l, r - l); + if (pref.back() == '#') got[n + 1 - sz(pref)] = pref; + for (auto [__, j] : next) { + self(self, pref, j); + } + }; + dfs(dfs, "", 0); + auto expected = naive(s); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 200'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + SuffixTree st(s); + t.stop(); + hash_t hash = sz(st.tree); + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/trie.cpp b/test/string/trie.cpp new file mode 100644 index 0000000..45d89cf --- /dev/null +++ b/test/string/trie.cpp @@ -0,0 +1,58 @@ +#include "../util.h" +#include <string/trie.cpp> + +void stress_test() { + multiset<vector<int>> naive; + ll queries = 0; + ll deleted = 0; + for (int tries = 0; tries < 100'000; tries++) { + { + int n = Random::integer<int>(1, 20); + auto s = Random::integers<int>(n, 0, 2); + insert(s); + naive.insert(s); + } + { + int n = Random::integer<int>(1, 20); + auto s = Random::integers<int>(n, 0, 2); + bool got = erase(s); + auto it = naive.find(s); + bool expected = it != naive.end(); + if (expected) naive.erase(it); + if (got != expected) cerr << "error" << FAIL; + queries++; + if (got) deleted++; + } + } + cerr << "tested random queries: " << queries << " (" << deleted << ")" << endl; +} + +constexpr int N = 10'000; +void performance_test() { + timer t; + trie = {node()}; + hash_t hash = 0; + for (int tries = 0; tries < N; tries++) { + { + int n = Random::integer<int>(1, 2000); + auto s = Random::integers<int>(n, 0, 2); + t.start(); + insert(s); + t.stop(); + } + { + int n = Random::integer<int>(1, 2000); + auto s = Random::integers<int>(n, 0, 2); + t.start(); + hash += erase(s); + t.stop(); + } + } + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/string/z.cpp b/test/string/z.cpp new file mode 100644 index 0000000..f890a3e --- /dev/null +++ b/test/string/z.cpp @@ -0,0 +1,41 @@ +#include "../util.h" +#include <string/z.cpp> + +vector<int> naive(const string& s) { + vector<int> res(sz(s)); + for (int i = 1; i < sz(s); i++) { + while (i + res[i] < sz(s) && s[res[i]] == s[i + res[i]]) res[i]++; + } + return res; +} + +void stress_test() { + ll queries = 0; + for (int tries = 0; tries < 100'000; tries++) { + int n = Random::integer<int>(1, 15); + auto s = Random::string(n, "abc"); + auto got = Z(s); + auto expected = naive(s); + if (got != expected) cerr << "error" << FAIL; + queries += n; + } + cerr << "tested random queries: " << queries << endl; +} + +constexpr int N = 10'000'000; +void performance_test() { + timer t; + auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#"); + t.start(); + auto res = Z(s); + t.stop(); + hash_t hash = 0; + for (int x : res) hash += x; + if (t.time > 500) cerr << "too slow: " << t.time << FAIL; + cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl; +} + +int main() { + stress_test(); + performance_test(); +} diff --git a/test/template/template.cpp b/test/template/template.cpp new file mode 100644 index 0000000..db9aa00 --- /dev/null +++ b/test/template/template.cpp @@ -0,0 +1 @@ +#include <template/template.cpp> diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000..0ca230b --- /dev/null +++ b/test/test.sh @@ -0,0 +1,70 @@ +#!/bin/bash +set -e +cd "$(dirname "$0")" +ulimit -s 4000000 +export MALLOC_PERTURB_="$((2#01011001))" + +declare -A cppstandard +cppstandard["string/suffixArray.cpp"]="gnu++20" + +test_file() { + file=$(realpath --relative-to="${PWD}" "${1}") + echo "$file:" + echo "compiling..." + std="gnu++17" + if [[ -v cppstandard[$file] ]]; then + std=${cppstandard[$file]} + fi + g++ -std=$std "$file" -I ../content/ -O2 -Wall -Wextra -Wshadow -Werror + echo "running..." + timeout --foreground 60s ./a.out + echo "" + rm ./a.out +} + +list_missing() { + declare -A ignore + ignore["datastructures/stlPriorityQueue.cpp"]=1 + ignore["datastructures/stlRope.cpp"]=1 + ignore["other/bitOps.cpp"]=1 + ignore["other/pbs.cpp"]=1 + ignore["other/pragmas.cpp"]=1 + ignore["other/stuff.cpp"]=1 + ignore["other/timed.cpp"]=1 + ignore["tests/gcc5bug.cpp"]=1 + ignore["tests/precision.cpp"]=1 + ignore["tests/whitespace.cpp"]=1 + + echo "missing tests:" + find ../content/ -type f -name '*.cpp' -print0 | sort -z | while read -d $'\0' file + do + file=${file#../content/} + if [ ! -f "$file" ] && [[ ! -v ignore["$file"] ]]; then + echo " $file" + fi + done +} + +if [ "$#" -ne 0 ]; then + for arg in "$@" + do + if [[ "$arg" == "--missing" ]]; then + list_missing + elif [ -d "$arg" ]; then + dir=$(realpath --relative-to="${PWD}" "$arg") + find . -type f -path "./${dir}/*.cpp" -print0 | sort -z | while read -d $'\0' file + do + test_file "$file" + done + elif [ -f "$arg" ]; then + test_file "$arg" + fi + done +else + find . -type f -path '*.cpp' -print0 | sort -z | while read -d $'\0' file + do + test_file "$file" + done + list_missing +fi + diff --git a/test/util.h b/test/util.h new file mode 100644 index 0000000..bfa35d8 --- /dev/null +++ b/test/util.h @@ -0,0 +1,411 @@ +#include <bits/stdc++.h> +using namespace std; + +#define all(x) std::begin(x), std::end(x) +#define sz(x) (ll)std::size(x) + +using ll = long long; +using lll = __int128; +using ld = long double; +using hash_t = unsigned long long; + +namespace INT {constexpr int INF = 0x3FFF'FFFF;} +namespace LL {constexpr ll INF = 0x3FFF'FFFF'FFFF'FFFFll;} +namespace LD {constexpr ld INF = numeric_limits<ld>::infinity();} + +namespace details { + template<typename T = ll> + bool isPrime(T x) { + for (T i = 2; i*i <= x; i++) { + if (x % i == 0) return false; + } + return x > 1; + } +} + +namespace Random { + mt19937_64 rng(3141592653589793238ll); + template<typename T = ll> + T integer(T l, T r) { + return uniform_int_distribution<T>(l, r-1)(rng); + } + + template<typename T = ll> + T integer(T r) { + return integer<T>(0, r); + } + + template<typename T = ll> + std::vector<T> integers(std::size_t n, T l, T r) { + std::vector<T> res(n); + for (T& x : res) x = integer<T>(l, r); + return res; + } + + template<typename T = ll> + std::vector<T> integers(std::size_t n, T r) { + return integers<T>(n, 0, r); + } + + template<typename T = ll> + std::vector<T> distinct(std::size_t n, T l, T r) { + std::map<T, T> used; + std::vector<T> res; + for (std::size_t i = 0; i < n; i++) { + T x = integer<T>(l, r - i); + auto it = used.find(x); + if (it != used.end()) res.emplace_back(it->second); + else res.emplace_back(x); + it = used.find(r - i - 1); + if (it != used.end()) used[x] = it->second; + else used[x] = r - i - 1; + } + return res; + } + + template<typename T = ll> + std::vector<T> distinct(std::size_t n, T r) { + return distinct<T>(n, 0, r); + } + + template<typename T = ld> + T real(T l, T r) { + return uniform_real_distribution<T>(l, r)(rng); + } + + template<typename T = ld> + T real(T r) { + return real<T>(0, r); + } + + template<typename T = ld> + std::vector<T> reals(std::size_t n, T l, T r) { + std::vector<T> res(n); + for (T& x : res) x = real<T>(l, r); + return res; + } + + template<typename T = ld> + std::vector<T> reals(std::size_t n, T r) { + return reals<T>(n, 0, r); + } + + template<typename T = ll> + std::pair<T, T> pair(T l, T r) { + T a = integer<T>(l, r); + T b = integer<T>(l, r); + if (a > b) swap(a, b); + return {a, b}; + } + + template<typename T = ll> + std::pair<T, T> pair(T r) { + return pair<T>(0, r); + } + + std::string string(std::size_t n, string_view chars) { + std::string res(n, '*'); + for (char& c : res) c = chars[integer(sz(chars))]; + return res; + } + + template<typename T = ll> + T prime(T l, T r) { + T res = 0; + do { + res = integer<T>(l, r); + } while (!details::isPrime<T>(res)); + return res; + } + + template<typename T = ll> + T prime(T r) { + return prime<T>(2, r); + } + + template<typename T = ll, typename std::enable_if<!std::is_floating_point<T>::value>::type* = nullptr> + std::complex<T> point(T l, T r) { + return {integer<T>(l, r), integer<T>(l, r)}; + } + + template<typename T = ld, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> + std::complex<T> point(T l, T r) { + return {real(l, r), real(l, r)}; + } + + template<typename T = ll> + std::complex<T> point(T r) { + return point<T>(0, r); + } + + template<typename T = ll, typename std::enable_if<!std::is_floating_point<T>::value>::type* = nullptr> + std::vector<std::complex<T>> points(std::size_t n, T l, T r) { + std::vector<std::complex<T>> res(n); + for (auto& x : res) x = point<T>(l, r); + return res; + } + + template<typename T = ld, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> + std::vector<std::complex<T>> points(std::size_t n, T l, T r) { + std::vector<std::complex<T>> res(n); + for (auto& x : res) x = point<T>(l, r); + return res; + } + + template<typename T = ll> + std::vector<std::complex<T>> points(std::size_t n, T r) { + return points<T>(n, 0, r); + } + +} + +[[noreturn]] ostream& FAIL(ostream& os) { + os << endl; + exit(1); +} + +struct timer { + bool running = false; + double time = 0; + chrono::steady_clock::time_point begin; + + void start() { + if (running) cerr << "timer already running!" << FAIL; + running = true; + begin = chrono::steady_clock::now(); + } + + void stop() { + auto end = chrono::steady_clock::now(); + if (!running) cerr << "timer not running!" << FAIL; + running = false; + time += chrono::duration_cast<chrono::duration<double, milli>>(end - begin).count(); + } + + void reset() { + running = false; + time = 0; + } + + ~timer() { + if (running) cerr << "timer not stopped!" << FAIL; + } + + friend std::ostream& operator<<(std::ostream& os, const timer& t) { + if (t.running) cerr << "timer already running!" << FAIL; + return os << t.time; + } +}; + +namespace c20 { + namespace detail { + template<class T, std::size_t N, std::size_t... I> + constexpr std::array<std::remove_cv_t<T>, N> to_array_impl(T (&a)[N], std::index_sequence<I...>) { + return {{a[I]...}}; + } + } + + template<class T, std::size_t N> + constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) { + return c20::detail::to_array_impl(a, std::make_index_sequence<N>{}); + } +} + +struct NoData {}; + +template<typename W = NoData> +struct Edge { + int from, to; + mutable W w; + + Edge(int from_, int to_, W w_ = {}) : from(from_), to(to_), w(w_) {} + + template<typename F> + void forE(int x, F&& f) const { + int y = from ^ to ^ x; + if constexpr (is_same_v<W, NoData>) f(x, y); + else f(x, y, w); + } + + template<typename F> + void forE(F&& f) const { + forE(from, f); + } + + friend std::ostream& operator<<(std::ostream& os, const Edge& e) { + os << e.from << "->" << e.to; + if constexpr (!is_same_v<W, NoData>) os << ": " << e.w; + return os; + } +}; + + +template<typename W = NoData, bool DIR = false, bool MULTI = false, bool LOOPS = false> +class Graph { + vector<multimap<int, int>> adj; + vector<Edge<W>> edges; + + void _addAdj(int e) { + adj[edges[e].from].emplace(edges[e].to, e); + if (!DIR && edges[e].from != edges[e].to) adj[edges[e].to].emplace(edges[e].from, e); + } +public: + + Graph(int n) : adj(n) {} + + int m() const {return sz(edges);} + int n() const {return sz(adj);} + int deg(int x) const {return sz(adj[x]);} + + Graph& clear() { + adj.assign(adj.size(), {}); + edges.clear(); + return *this; + } + + bool addEdge(int from, int to, W w = {}) { + if (!LOOPS && from == to) return false; + if (!MULTI && adj[from].find(to) != adj[from].end()) return false; + edges.emplace_back(from, to, w); + _addAdj(sz(edges) - 1); + return true; + } + + Graph& reverse() { + for (auto& e : edges) swap(e.from, e.to); + adj.assign(adj.size(), {}); + for (int i = 0; i < sz(edges); i++) _addAdj(i); + return *this; + } + + Graph& shuffle() { + std::shuffle(all(edges), Random::rng); + if constexpr (!DIR) { + for (auto& e : edges) { + if (Random::integer(0, 2)) swap(e.from, e.to); + } + } + adj.assign(adj.size(), {}); + for (int i = 0; i < sz(edges); i++) _addAdj(i); + return *this; + } + + Graph& permutate() { + vector<int> perm(n()); + iota(all(perm), 0); + std::shuffle(all(perm), Random::rng); + for (auto& e : edges) { + e.from = perm[e.from]; + e.to = perm[e.to]; + } + shuffle(); + return *this; + } + + template<typename F> + void forEdges(F&& f) { + for (auto e : edges) e.forE(f); + } + + template<typename F> + void forOut(int x, F&& f) { + for (auto [_, id] : adj[x]) edges[id].forE(x, f); + } + + Graph& tree() { + if (DIR) cerr << "randomTree must be undirected" << FAIL; + if (n() <= 1) return *this; + auto code = Random::integers<int>(n()-2, 0, n()); + vector<int> count(n()); + for (int x : code) count[x]++; + int current = -1; + int skip = -1; + auto getNext = [&](){ + int oldSkip = skip; + skip = -1; + if (0 <= oldSkip and oldSkip <= current) return oldSkip; + for (current++; count[current] > 0; current++) {} + return current; + }; + for (int x : code) { + addEdge(x, getNext()); + count[x]--; + if (count[x] == 0) skip = x; + } + addEdge(getNext(), getNext()); + return *this; + } + + Graph& star() { + for (int i = 1; i < n; i++) addEdge(0, i); + return *this; + } + + Graph& path() { + for (int i = 1; i < n; i++) addEdge(i-1, i); + return *this; + } + + Graph& erdosRenyi(int todo) {//this could be slow... + if constexpr (!MULTI) { + ll lim = (ll)n() * (n() - 1) / 2; + if constexpr (DIR) lim *= 2; + if constexpr (LOOPS) lim += n; + lim -= m(); + if (todo > lim) cerr << "too many edges! n: " << n() << " " << todo << " > " << lim << FAIL; + if (todo > lim / 2) { + vector<pair<int, int>> tmp; + if constexpr(LOOPS) { + for (int i = 0; i < n(); i++) tmp.emplace_back(i, i); + } + for (int i = 0; i < n(); i++) { + for (int j = 0; j < i; j++) { + tmp.emplace_back(i, j); + if constexpr (DIR) tmp.emplace_back(j, i); + } + } + if constexpr (!DIR) { + for (auto& [a, b] : tmp) { + if (Random::integer<int>(0, 2) == 0) { + swap(a, b); + } + } + } + std::shuffle(all(tmp), Random::rng); + for (auto [a, b] : tmp) { + if (todo <= 0) break; + if (addEdge(a, b)) todo--; + } + if (todo > 0) cerr << "too many edges!" << FAIL; + return *this; + } + } + while (todo > 0) { + int a = Random::integer(0, n()); + int b = Random::integer(0, n()); + if (addEdge(a, b)) todo--; + } + return *this; + } + + friend std::ostream& operator<<(std::ostream& os, const Graph& g) { + os << g.n() << " " << g.m(); + for (auto& e : g.edges) os << endl << e; + return os; + } +}; + +ld float_error(ld given, ld expected) { + if (isfinite(given) && isfinite(expected)) { + ld absDiff = abs(given-expected); + ld relDiff = abs((given-expected)/expected); + return min(absDiff, relDiff); + } + if (isnan(given) && isnan(expected)) { + return 0; + } + if (isinf(given) && isinf(expected)) { + return signbit(given) == signbit(expected) ? 0 : numeric_limits<ld>::infinity(); + } + return numeric_limits<ld>::infinity(); +} |
