diff options
| -rw-r--r-- | .github/workflows/list_missing.yml | 9 | ||||
| -rw-r--r-- | .github/workflows/test_all.yml | 14 | ||||
| -rw-r--r-- | .github/workflows/test_datastructures.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_geometry.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_graph.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_math.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_other.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_pdf.yml | 25 | ||||
| -rw-r--r-- | .github/workflows/test_string.yml | 22 | ||||
| -rw-r--r-- | .github/workflows/test_template.yml | 22 | ||||
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | content/datastructures/LCT.cpp (renamed from datastructures/LCT.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/bitset.cpp (renamed from datastructures/bitset.cpp) | 2 | ||||
| -rw-r--r-- | content/datastructures/datastructures.tex (renamed from datastructures/datastructures.tex) | 37 | ||||
| -rw-r--r-- | content/datastructures/dynamicConvexHull.cpp (renamed from datastructures/dynamicConvexHull.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/fenwickTree.cpp | 15 | ||||
| -rw-r--r-- | content/datastructures/fenwickTree2.cpp (renamed from datastructures/fenwickTree2.cpp) | 12 | ||||
| -rw-r--r-- | content/datastructures/lazyPropagation.cpp (renamed from datastructures/lazyPropagation.cpp) | 12 | ||||
| -rw-r--r-- | content/datastructures/lichao.cpp (renamed from datastructures/lichao.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/monotonicConvexHull.cpp (renamed from datastructures/monotonicConvexHull.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/pbds.cpp (renamed from datastructures/pbds.cpp) | 2 | ||||
| -rw-r--r-- | content/datastructures/persistent.cpp (renamed from datastructures/persistent.cpp) | 2 | ||||
| -rw-r--r-- | content/datastructures/persistentArray.cpp (renamed from datastructures/persistentArray.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/segmentTree.cpp (renamed from datastructures/segmentTree.cpp) | 2 | ||||
| -rw-r--r-- | content/datastructures/sparseTable.cpp (renamed from datastructures/sparseTable.cpp) | 3 | ||||
| -rw-r--r-- | content/datastructures/sparseTableDisjoint.cpp (renamed from datastructures/sparseTableDisjoint.cpp) | 5 | ||||
| -rw-r--r-- | content/datastructures/stlHashMap.cpp (renamed from datastructures/stlHashMap.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/stlPriorityQueue.cpp (renamed from datastructures/stlPriorityQueue.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/stlRope.cpp (renamed from datastructures/stlRope.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/stlTree.cpp (renamed from datastructures/stlTree.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/treap.cpp (renamed from datastructures/treap.cpp) | 0 | ||||
| -rw-r--r-- | content/datastructures/treap2.cpp (renamed from datastructures/treap2.cpp) | 28 | ||||
| -rw-r--r-- | content/datastructures/unionFind.cpp (renamed from datastructures/unionFind.cpp) | 6 | ||||
| -rw-r--r-- | content/datastructures/waveletTree.cpp (renamed from datastructures/waveletTree.cpp) | 2 | ||||
| -rw-r--r-- | content/geometry/antipodalPoints.cpp (renamed from geometry/antipodalPoints.cpp) | 0 | ||||
| -rw-r--r-- | content/geometry/circle.cpp (renamed from geometry/circle.cpp) | 14 | ||||
| -rw-r--r-- | content/geometry/closestPair.cpp | 27 | ||||
| -rw-r--r-- | content/geometry/convexHull.cpp (renamed from geometry/convexHull.cpp) | 15 | ||||
| -rw-r--r-- | content/geometry/delaunay.cpp (renamed from geometry/delaunay.cpp) | 10 | ||||
| -rw-r--r-- | content/geometry/formulas.cpp (renamed from geometry/formulars.cpp) | 12 | ||||
| -rw-r--r-- | content/geometry/formulas3d.cpp (renamed from geometry/formulars3d.cpp) | 12 | ||||
| -rw-r--r-- | content/geometry/geometry.tex (renamed from geometry/geometry.tex) | 24 | ||||
| -rw-r--r-- | content/geometry/hpi.cpp (renamed from geometry/hpi.cpp) | 0 | ||||
| -rw-r--r-- | content/geometry/lines.cpp (renamed from geometry/lines.cpp) | 0 | ||||
| -rw-r--r-- | content/geometry/linesAndSegments.cpp | 89 | ||||
| -rw-r--r-- | content/geometry/polygon.cpp (renamed from geometry/polygon.cpp) | 30 | ||||
| -rw-r--r-- | content/geometry/segmentIntersection.cpp (renamed from geometry/segmentIntersection.cpp) | 4 | ||||
| -rw-r--r-- | content/geometry/sortAround.cpp (renamed from geometry/sortAround.cpp) | 1 | ||||
| -rw-r--r-- | content/geometry/spheres.cpp (renamed from geometry/spheres.cpp) | 6 | ||||
| -rw-r--r-- | content/geometry/triangle.cpp (renamed from geometry/triangle.cpp) | 10 | ||||
| -rw-r--r-- | content/geometry/triangle.tex (renamed from geometry/triangle.tex) | 0 | ||||
| -rw-r--r-- | content/graph/2sat.cpp (renamed from graph/2sat.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/LCA_sparse.cpp (renamed from graph/LCA_sparse.cpp) | 4 | ||||
| -rw-r--r-- | content/graph/TSP.cpp (renamed from graph/TSP.cpp) | 7 | ||||
| -rw-r--r-- | content/graph/articulationPoints.cpp (renamed from graph/articulationPoints.cpp) | 12 | ||||
| -rw-r--r-- | content/graph/bellmannFord.cpp (renamed from graph/bellmannFord.cpp) | 10 | ||||
| -rw-r--r-- | content/graph/bitonicTSP.cpp (renamed from graph/bitonicTSP.cpp) | 4 | ||||
| -rw-r--r-- | content/graph/bitonicTSPsimple.cpp (renamed from graph/bitonicTSPsimple.cpp) | 5 | ||||
| -rw-r--r-- | content/graph/blossom.cpp (renamed from graph/blossom.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/bronKerbosch.cpp (renamed from graph/bronKerbosch.cpp) | 6 | ||||
| -rw-r--r-- | content/graph/centroid.cpp (renamed from graph/centroid.cpp) | 2 | ||||
| -rw-r--r-- | content/graph/connect.cpp (renamed from graph/connect.cpp) | 2 | ||||
| -rw-r--r-- | content/graph/cycleCounting.cpp (renamed from graph/cycleCounting.cpp) | 18 | ||||
| -rw-r--r-- | content/graph/dfs.tex (renamed from graph/dfs.tex) | 0 | ||||
| -rw-r--r-- | content/graph/dijkstra.cpp (renamed from graph/dijkstra.cpp) | 4 | ||||
| -rw-r--r-- | content/graph/dinicScaling.cpp (renamed from graph/dinicScaling.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/euler.cpp (renamed from graph/euler.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/floydWarshall.cpp (renamed from graph/floydWarshall.cpp) | 17 | ||||
| -rw-r--r-- | content/graph/graph.tex (renamed from graph/graph.tex) | 11 | ||||
| -rw-r--r-- | content/graph/havelHakimi.cpp (renamed from graph/havelHakimi.cpp) | 2 | ||||
| -rw-r--r-- | content/graph/hld.cpp (renamed from graph/hld.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/hopcroftKarp.cpp (renamed from graph/hopcroftKarp.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/kruskal.cpp (renamed from graph/kruskal.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/matching.cpp (renamed from graph/matching.cpp) | 2 | ||||
| -rw-r--r-- | content/graph/maxCarBiMatch.cpp (renamed from graph/maxCarBiMatch.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/maxWeightBipartiteMatching.cpp (renamed from graph/maxWeightBipartiteMatching.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/minCostMaxFlow.cpp (renamed from graph/minCostMaxFlow.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/pushRelabel.cpp (renamed from graph/pushRelabel.cpp) | 4 | ||||
| -rw-r--r-- | content/graph/reroot.cpp (renamed from graph/reroot.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/scc.cpp (renamed from graph/scc.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/stoerWagner.cpp (renamed from graph/stoerWagner.cpp) | 0 | ||||
| -rw-r--r-- | content/graph/treeIsomorphism.cpp (renamed from graph/treeIsomorphism.cpp) | 2 | ||||
| -rw-r--r-- | content/graph/virtualTree.cpp (renamed from graph/virtualTree.cpp) | 8 | ||||
| -rw-r--r-- | content/latexHeaders/code.sty (renamed from latexHeaders/code.sty) | 18 | ||||
| -rw-r--r-- | content/latexHeaders/commands.sty (renamed from latexHeaders/commands.sty) | 0 | ||||
| -rw-r--r-- | content/latexHeaders/layout.sty (renamed from latexHeaders/layout.sty) | 0 | ||||
| -rw-r--r-- | content/latexHeaders/math.sty (renamed from latexHeaders/math.sty) | 0 | ||||
| -rw-r--r-- | content/math/berlekampMassey.cpp (renamed from math/berlekampMassey.cpp) | 0 | ||||
| -rw-r--r-- | content/math/bigint.cpp (renamed from math/bigint.cpp) | 24 | ||||
| -rw-r--r-- | content/math/binomial0.cpp (renamed from math/binomial0.cpp) | 2 | ||||
| -rw-r--r-- | content/math/binomial1.cpp (renamed from math/binomial1.cpp) | 0 | ||||
| -rw-r--r-- | content/math/binomial2.cpp (renamed from math/binomial2.cpp) | 0 | ||||
| -rw-r--r-- | content/math/binomial3.cpp (renamed from math/binomial3.cpp) | 2 | ||||
| -rw-r--r-- | content/math/chineseRemainder.cpp (renamed from math/chineseRemainder.cpp) | 0 | ||||
| -rw-r--r-- | content/math/cycleDetection.cpp (renamed from math/cycleDetection.cpp) | 6 | ||||
| -rw-r--r-- | content/math/discreteLogarithm.cpp | 17 | ||||
| -rw-r--r-- | content/math/discreteNthRoot.cpp | 5 | ||||
| -rw-r--r-- | content/math/divisors.cpp (renamed from math/divisors.cpp) | 0 | ||||
| -rw-r--r-- | content/math/extendedEuclid.cpp (renamed from math/extendedEuclid.cpp) | 0 | ||||
| -rw-r--r-- | content/math/gauss.cpp (renamed from math/gauss.cpp) | 2 | ||||
| -rw-r--r-- | content/math/gcd-lcm.cpp (renamed from math/gcd-lcm.cpp) | 0 | ||||
| -rw-r--r-- | content/math/goldenSectionSearch.cpp (renamed from math/goldenSectionSearch.cpp) | 8 | ||||
| -rw-r--r-- | content/math/inversions.cpp (renamed from math/inversions.cpp) | 0 | ||||
| -rw-r--r-- | content/math/inversionsMerge.cpp (renamed from math/inversionsMerge.cpp) | 0 | ||||
| -rw-r--r-- | content/math/kthperm.cpp (renamed from math/kthperm.cpp) | 2 | ||||
| -rw-r--r-- | content/math/legendre.cpp (renamed from math/legendre.cpp) | 2 | ||||
| -rw-r--r-- | content/math/lgsFp.cpp (renamed from math/lgsFp.cpp) | 4 | ||||
| -rw-r--r-- | content/math/linearCongruence.cpp (renamed from math/linearCongruence.cpp) | 0 | ||||
| -rw-r--r-- | content/math/linearRecurence.cpp (renamed from math/linearRecurence.cpp) | 0 | ||||
| -rw-r--r-- | content/math/linearSieve.cpp (renamed from math/linearSieve.cpp) | 29 | ||||
| -rw-r--r-- | content/math/longestIncreasingSubsequence.cpp (renamed from math/longestIncreasingSubsequence.cpp) | 0 | ||||
| -rw-r--r-- | content/math/math.tex (renamed from math/math.tex) | 272 | ||||
| -rw-r--r-- | content/math/matrixPower.cpp (renamed from math/matrixPower.cpp) | 8 | ||||
| -rw-r--r-- | content/math/millerRabin.cpp (renamed from math/millerRabin.cpp) | 0 | ||||
| -rw-r--r-- | content/math/modExp.cpp (renamed from math/modExp.cpp) | 0 | ||||
| -rw-r--r-- | content/math/modMulIterativ.cpp (renamed from math/modMulIterativ.cpp) | 0 | ||||
| -rw-r--r-- | content/math/modPowIterativ.cpp (renamed from math/modPowIterativ.cpp) | 0 | ||||
| -rw-r--r-- | content/math/multInv.cpp (renamed from math/multInv.cpp) | 0 | ||||
| -rw-r--r-- | content/math/permIndex.cpp (renamed from math/permIndex.cpp) | 0 | ||||
| -rw-r--r-- | content/math/piLegendre.cpp (renamed from math/piLegendre.cpp) | 0 | ||||
| -rw-r--r-- | content/math/piLehmer.cpp (renamed from math/piLehmer.cpp) | 2 | ||||
| -rw-r--r-- | content/math/polynomial.cpp (renamed from math/polynomial.cpp) | 0 | ||||
| -rw-r--r-- | content/math/primeSieve.cpp (renamed from math/primeSieve.cpp) | 0 | ||||
| -rw-r--r-- | content/math/primitiveRoot.cpp | 23 | ||||
| -rw-r--r-- | content/math/rho.cpp (renamed from math/rho.cpp) | 4 | ||||
| -rw-r--r-- | content/math/shortModInv.cpp | 3 | ||||
| -rw-r--r-- | content/math/simpson.cpp (renamed from math/simpson.cpp) | 2 | ||||
| -rw-r--r-- | content/math/sqrtModCipolla.cpp | 14 | ||||
| -rw-r--r-- | content/math/squfof.cpp (renamed from math/squfof.cpp) | 0 | ||||
| -rw-r--r-- | content/math/tables.tex (renamed from math/tables.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/binom.tex (renamed from math/tables/binom.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/composite.tex (renamed from math/tables/composite.tex) | 42 | ||||
| -rw-r--r-- | content/math/tables/nim.tex (renamed from math/tables/nim.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/numbers.tex (renamed from math/tables/numbers.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/platonic.tex (renamed from math/tables/platonic.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/probability.tex (renamed from math/tables/probability.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/series.tex (renamed from math/tables/series.tex) | 0 | ||||
| -rw-r--r-- | content/math/tables/stuff.tex (renamed from math/tables/stuff.tex) | 2 | ||||
| -rw-r--r-- | content/math/tables/twelvefold.tex (renamed from math/tables/twelvefold.tex) | 0 | ||||
| -rw-r--r-- | content/math/transforms/andTransform.cpp (renamed from math/transforms/andTransform.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/bitwiseTransforms.cpp (renamed from math/transforms/bitwiseTransforms.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/fft.cpp (renamed from math/transforms/fft.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/fftMul.cpp | 15 | ||||
| -rw-r--r-- | content/math/transforms/multiplyBitwise.cpp (renamed from math/transforms/multiplyBitwise.cpp) | 2 | ||||
| -rw-r--r-- | content/math/transforms/multiplyFFT.cpp (renamed from math/transforms/multiplyFFT.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/multiplyNTT.cpp (renamed from math/transforms/multiplyNTT.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/ntt.cpp (renamed from math/transforms/ntt.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/orTransform.cpp (renamed from math/transforms/orTransform.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/seriesOperations.cpp (renamed from math/transforms/seriesOperations.cpp) | 0 | ||||
| -rw-r--r-- | content/math/transforms/xorTransform.cpp (renamed from math/transforms/xorTransform.cpp) | 0 | ||||
| -rw-r--r-- | content/other/bitOps.cpp (renamed from other/bitOps.cpp) | 0 | ||||
| -rw-r--r-- | content/other/compiletime.cpp (renamed from other/compiletime.cpp) | 0 | ||||
| -rw-r--r-- | content/other/divideAndConquer.cpp (renamed from other/divideAndConquer.cpp) | 18 | ||||
| -rw-r--r-- | content/other/fastIO.cpp (renamed from other/fastIO.cpp) | 2 | ||||
| -rw-r--r-- | content/other/josephus2.cpp (renamed from other/josephus2.cpp) | 0 | ||||
| -rw-r--r-- | content/other/josephusK.cpp (renamed from other/josephusK.cpp) | 0 | ||||
| -rw-r--r-- | content/other/knuth.cpp (renamed from other/knuth.cpp) | 10 | ||||
| -rw-r--r-- | content/other/other.tex (renamed from other/other.tex) | 8 | ||||
| -rw-r--r-- | content/other/pbs.cpp (renamed from other/pbs.cpp) | 0 | ||||
| -rw-r--r-- | content/other/pragmas.cpp (renamed from other/pragmas.cpp) | 0 | ||||
| -rw-r--r-- | content/other/sos.cpp (renamed from other/sos.cpp) | 0 | ||||
| -rw-r--r-- | content/other/split.cpp | 10 | ||||
| -rw-r--r-- | content/other/stress.sh (renamed from other/stress.sh) | 0 | ||||
| -rw-r--r-- | content/other/stuff.cpp (renamed from other/stuff.cpp) | 0 | ||||
| -rw-r--r-- | content/other/timed.cpp (renamed from other/timed.cpp) | 0 | ||||
| -rw-r--r-- | content/python/io.py (renamed from python/io.py) | 0 | ||||
| -rw-r--r-- | content/python/python.tex | 10 | ||||
| -rw-r--r-- | content/python/recursion.py (renamed from python/recursion.py) | 0 | ||||
| -rw-r--r-- | content/string/ahoCorasick.cpp (renamed from string/ahoCorasick.cpp) | 0 | ||||
| -rw-r--r-- | content/string/deBruijn.cpp (renamed from string/deBruijn.cpp) | 0 | ||||
| -rw-r--r-- | content/string/duval.cpp (renamed from string/duval.cpp) | 0 | ||||
| -rw-r--r-- | content/string/kmp.cpp (renamed from string/kmp.cpp) | 0 | ||||
| -rw-r--r-- | content/string/longestCommonSubsequence.cpp (renamed from string/longestCommonSubsequence.cpp) | 2 | ||||
| -rw-r--r-- | content/string/lyndon.cpp (renamed from string/lyndon.cpp) | 4 | ||||
| -rw-r--r-- | content/string/manacher.cpp (renamed from string/manacher.cpp) | 0 | ||||
| -rw-r--r-- | content/string/rollingHash.cpp (renamed from string/rollingHash2.cpp) | 2 | ||||
| -rw-r--r-- | content/string/rollingHashCf.cpp | 17 | ||||
| -rw-r--r-- | content/string/string.tex (renamed from string/string.tex) | 4 | ||||
| -rw-r--r-- | content/string/suffixArray.cpp (renamed from string/suffixArray.cpp) | 0 | ||||
| -rw-r--r-- | content/string/suffixAutomaton.cpp (renamed from string/suffixAutomaton.cpp) | 30 | ||||
| -rw-r--r-- | content/string/suffixTree.cpp (renamed from string/suffixTree.cpp) | 22 | ||||
| -rw-r--r-- | content/string/trie.cpp | 35 | ||||
| -rw-r--r-- | content/string/z.cpp (renamed from string/z.cpp) | 0 | ||||
| -rw-r--r-- | content/tcr.tex (renamed from tcr.tex) | 0 | ||||
| -rw-r--r-- | content/template/console.sh (renamed from template/console.cpp) | 0 | ||||
| -rw-r--r-- | content/template/template.cpp (renamed from template/template.cpp) | 2 | ||||
| -rw-r--r-- | content/template/template.tex (renamed from template/template.tex) | 2 | ||||
| -rw-r--r-- | content/tests/gcc5bug.cpp (renamed from tests/gcc5bug.cpp) | 0 | ||||
| -rw-r--r-- | content/tests/precision.cpp (renamed from tests/precision.cpp) | 0 | ||||
| -rw-r--r-- | content/tests/test.tex (renamed from tests/test.tex) | 0 | ||||
| -rw-r--r-- | content/tests/whitespace.cpp (renamed from tests/whitespace.cpp) | 0 | ||||
| -rw-r--r-- | datastructures/RMQ.cpp | 27 | ||||
| -rw-r--r-- | datastructures/fenwickTree.cpp | 15 | ||||
| -rw-r--r-- | datastructures/firstUnused.cpp | 13 | ||||
| -rw-r--r-- | datastructures/stlPQ.cpp | 15 | ||||
| -rw-r--r-- | datastructures/unionFind2.cpp | 25 | ||||
| -rw-r--r-- | geometry/closestPair.cpp | 38 | ||||
| -rw-r--r-- | geometry/linesAndSegments.cpp | 89 | ||||
| -rw-r--r-- | graph/LCA.cpp | 24 | ||||
| -rw-r--r-- | graph/capacityScaling.cpp | 44 | ||||
| -rw-r--r-- | math/discreteLogarithm.cpp | 14 | ||||
| -rw-r--r-- | math/discreteNthRoot.cpp | 5 | ||||
| -rw-r--r-- | math/mobius.cpp | 21 | ||||
| -rw-r--r-- | math/modSqrt.cpp | 23 | ||||
| -rw-r--r-- | math/phi.cpp | 21 | ||||
| -rw-r--r-- | math/primitiveRoot.cpp | 23 | ||||
| -rw-r--r-- | math/shortModInv.cpp | 3 | ||||
| -rw-r--r-- | math/sqrtModCipolla.cpp | 13 | ||||
| -rw-r--r-- | math/transforms/fftMul.cpp | 14 | ||||
| -rw-r--r-- | other/split.cpp | 10 | ||||
| -rw-r--r-- | python/python.tex | 10 | ||||
| -rw-r--r-- | string/rollingHash.cpp | 16 | ||||
| -rw-r--r-- | string/rollingHashCf.cpp | 17 | ||||
| -rw-r--r-- | string/trie.cpp | 33 | ||||
| -rw-r--r-- | tcr.pdf | bin | 667098 -> 690769 bytes | |||
| -rw-r--r-- | test/datastructures/bitset.cpp | 6 | ||||
| -rw-r--r-- | test/datastructures/fenwickTree.cpp | 58 | ||||
| -rw-r--r-- | test/datastructures/fenwickTree2.cpp | 60 | ||||
| -rw-r--r-- | test/datastructures/lazyPropagation.cpp | 61 | ||||
| -rw-r--r-- | test/datastructures/pbds.cpp | 11 | ||||
| -rw-r--r-- | test/datastructures/segmentTree.cpp | 122 | ||||
| -rw-r--r-- | test/datastructures/sparseTable.cpp | 51 | ||||
| -rw-r--r-- | test/datastructures/sparseTableDisjoint.cpp | 48 | ||||
| -rw-r--r-- | test/datastructures/stlHashMap.cpp | 4 | ||||
| -rw-r--r-- | test/datastructures/stlTree.cpp | 2 | ||||
| -rw-r--r-- | test/datastructures/unionFind.cpp | 109 | ||||
| -rw-r--r-- | test/datastructures/waveletTree.cpp | 75 | ||||
| -rw-r--r-- | test/geometry.h | 140 | ||||
| -rw-r--r-- | test/geometry/antipodalPoints.cpp | 70 | ||||
| -rw-r--r-- | test/geometry/circle.cpp | 116 | ||||
| -rw-r--r-- | test/geometry/closestPair.cpp | 69 | ||||
| -rw-r--r-- | test/geometry/closestPair.double.cpp | 66 | ||||
| -rw-r--r-- | test/geometry/convexHull.cpp | 79 | ||||
| -rw-r--r-- | test/geometry/delaunay.cpp | 144 | ||||
| -rw-r--r-- | test/geometry/formulas.cpp | 127 | ||||
| -rw-r--r-- | test/geometry/linesAndSegments.cpp | 240 | ||||
| -rw-r--r-- | test/geometry/polygon.cpp | 296 | ||||
| -rw-r--r-- | test/geometry/segmentIntersection.cpp | 88 | ||||
| -rw-r--r-- | test/geometry/sortAround.cpp | 83 | ||||
| -rw-r--r-- | test/geometry/triangle.cpp | 146 | ||||
| -rw-r--r-- | test/graph/2sat.cpp | 133 | ||||
| -rw-r--r-- | test/graph/LCA_sparse.cpp | 63 | ||||
| -rw-r--r-- | test/graph/TSP.cpp | 67 | ||||
| -rw-r--r-- | test/graph/articulationPoints.bcc.cpp | 78 | ||||
| -rw-r--r-- | test/graph/articulationPoints.bridges.cpp | 64 | ||||
| -rw-r--r-- | test/graph/articulationPoints.cpp | 85 | ||||
| -rw-r--r-- | test/graph/bellmannFord.cpp | 70 | ||||
| -rw-r--r-- | test/graph/bitonicTSP.cpp | 49 | ||||
| -rw-r--r-- | test/graph/bitonicTSPsimple.cpp | 49 | ||||
| -rw-r--r-- | test/graph/blossom.cpp | 76 | ||||
| -rw-r--r-- | test/graph/bronKerbosch.cpp | 73 | ||||
| -rw-r--r-- | test/graph/centroid.cpp | 77 | ||||
| -rw-r--r-- | test/graph/cycleCounting.cpp | 79 | ||||
| -rw-r--r-- | test/graph/dijkstra.cpp | 64 | ||||
| -rw-r--r-- | test/graph/dinicScaling.cpp | 61 | ||||
| -rw-r--r-- | test/graph/euler.cpp | 87 | ||||
| -rw-r--r-- | test/graph/floydWarshall.cpp | 90 | ||||
| -rw-r--r-- | test/graph/havelHakimi.cpp | 65 | ||||
| -rw-r--r-- | test/graph/hopcroftKarp.cpp | 74 | ||||
| -rw-r--r-- | test/graph/kruskal.cpp | 91 | ||||
| -rw-r--r-- | test/graph/matching.cpp | 62 | ||||
| -rw-r--r-- | test/graph/maxCarBiMatch.cpp | 74 | ||||
| -rw-r--r-- | test/graph/maxWeightBipartiteMatching.cpp | 59 | ||||
| -rw-r--r-- | test/graph/minCostMaxFlow.cpp | 68 | ||||
| -rw-r--r-- | test/graph/pushRelabel.cpp | 61 | ||||
| -rw-r--r-- | test/graph/scc.cpp | 92 | ||||
| -rw-r--r-- | test/graph/stoerWagner.cpp | 81 | ||||
| -rw-r--r-- | test/graph/treeIsomorphism.cpp | 126 | ||||
| -rw-r--r-- | test/math/berlekampMassey.cpp | 68 | ||||
| -rw-r--r-- | test/math/bigint.cpp | 122 | ||||
| -rw-r--r-- | test/math/binomial0.cpp | 31 | ||||
| -rw-r--r-- | test/math/binomial1.cpp | 27 | ||||
| -rw-r--r-- | test/math/binomial2.cpp | 29 | ||||
| -rw-r--r-- | test/math/binomial3.cpp | 31 | ||||
| -rw-r--r-- | test/math/chineseRemainder.cpp | 47 | ||||
| -rw-r--r-- | test/math/cycleDetection.cpp | 47 | ||||
| -rw-r--r-- | test/math/discreteLogarithm.cpp | 64 | ||||
| -rw-r--r-- | test/math/discreteNthRoot.cpp | 78 | ||||
| -rw-r--r-- | test/math/divisors.cpp | 65 | ||||
| -rw-r--r-- | test/math/extendedEuclid.cpp | 41 | ||||
| -rw-r--r-- | test/math/gauss.cpp | 118 | ||||
| -rw-r--r-- | test/math/gcd-lcm.cpp | 46 | ||||
| -rw-r--r-- | test/math/goldenSectionSearch.cpp | 74 | ||||
| -rw-r--r-- | test/math/inversions.cpp | 43 | ||||
| -rw-r--r-- | test/math/inversionsMerge.cpp | 46 | ||||
| -rw-r--r-- | test/math/kthperm.cpp | 38 | ||||
| -rw-r--r-- | test/math/kthperm_permIndex.cpp | 21 | ||||
| -rw-r--r-- | test/math/legendre.cpp | 43 | ||||
| -rw-r--r-- | test/math/lgsFp.cpp | 118 | ||||
| -rw-r--r-- | test/math/linearCongruence.cpp | 53 | ||||
| -rw-r--r-- | test/math/linearRecurence.cpp | 54 | ||||
| -rw-r--r-- | test/math/linearSieve.cpp | 71 | ||||
| -rw-r--r-- | test/math/longestIncreasingSubsequence.cpp | 76 | ||||
| -rw-r--r-- | test/math/matrixPower.cpp | 116 | ||||
| -rw-r--r-- | test/math/millerRabin.base32.cpp | 137 | ||||
| -rw-r--r-- | test/math/millerRabin.cpp | 129 | ||||
| -rw-r--r-- | test/math/modExp.cpp | 42 | ||||
| -rw-r--r-- | test/math/modMulIterativ.cpp | 57 | ||||
| -rw-r--r-- | test/math/modPowIterativ.cpp | 42 | ||||
| -rw-r--r-- | test/math/multInv.cpp | 40 | ||||
| -rw-r--r-- | test/math/permIndex.cpp | 39 | ||||
| -rw-r--r-- | test/math/piLegendre.cpp | 40 | ||||
| -rw-r--r-- | test/math/piLehmer.cpp | 42 | ||||
| -rw-r--r-- | test/math/primeSieve.cpp | 47 | ||||
| -rw-r--r-- | test/math/primitiveRoot.cpp | 82 | ||||
| -rw-r--r-- | test/math/rho.cpp | 117 | ||||
| -rw-r--r-- | test/math/shortModInv.cpp | 39 | ||||
| -rw-r--r-- | test/math/simpson.cpp | 63 | ||||
| -rw-r--r-- | test/math/sqrtModCipolla.cpp | 48 | ||||
| -rw-r--r-- | test/math/transforms/andTransform.cpp | 38 | ||||
| -rw-r--r-- | test/math/transforms/bitwiseTransforms.cpp | 38 | ||||
| -rw-r--r-- | test/math/transforms/fft.cpp | 51 | ||||
| -rw-r--r-- | test/math/transforms/fftMul.cpp | 62 | ||||
| -rw-r--r-- | test/math/transforms/multiplyBitwise.cpp | 55 | ||||
| -rw-r--r-- | test/math/transforms/multiplyFFT.cpp | 55 | ||||
| -rw-r--r-- | test/math/transforms/multiplyNTT.cpp | 56 | ||||
| -rw-r--r-- | test/math/transforms/ntt.cpp | 39 | ||||
| -rw-r--r-- | test/math/transforms/orTransform.cpp | 38 | ||||
| -rw-r--r-- | test/math/transforms/xorTransform.cpp | 38 | ||||
| -rw-r--r-- | test/other/compiletime.cpp | 2 | ||||
| -rw-r--r-- | test/other/divideAndConquer.cpp | 103 | ||||
| -rw-r--r-- | test/other/fastIO.cpp | 32 | ||||
| -rw-r--r-- | test/other/fastIO.in | 2 | ||||
| -rw-r--r-- | test/other/josephus2.cpp | 42 | ||||
| -rw-r--r-- | test/other/josephusK.cpp | 43 | ||||
| -rw-r--r-- | test/other/knuth.cpp | 103 | ||||
| -rw-r--r-- | test/other/sos.cpp | 50 | ||||
| -rw-r--r-- | test/other/split.cpp | 24 | ||||
| -rw-r--r-- | test/string/ahoCorasick.cpp | 76 | ||||
| -rw-r--r-- | test/string/deBruijn.cpp | 43 | ||||
| -rw-r--r-- | test/string/duval.cpp | 85 | ||||
| -rw-r--r-- | test/string/kmp.cpp | 85 | ||||
| -rw-r--r-- | test/string/longestCommonSubsequence.cpp | 55 | ||||
| -rw-r--r-- | test/string/lyndon.cpp | 61 | ||||
| -rw-r--r-- | test/string/manacher.cpp | 49 | ||||
| -rw-r--r-- | test/string/rollingHash.cpp | 92 | ||||
| -rw-r--r-- | test/string/rollingHashCf.cpp | 94 | ||||
| -rw-r--r-- | test/string/suffixArray.cpp | 61 | ||||
| -rw-r--r-- | test/string/suffixAutomaton.cpp | 62 | ||||
| -rw-r--r-- | test/string/suffixTree.cpp | 50 | ||||
| -rw-r--r-- | test/string/trie.cpp | 58 | ||||
| -rw-r--r-- | test/string/z.cpp | 41 | ||||
| -rw-r--r-- | test/template/template.cpp | 1 | ||||
| -rwxr-xr-x | test/test.sh | 70 | ||||
| -rw-r--r-- | test/util.h | 411 |
346 files changed, 10123 insertions, 977 deletions
diff --git a/.github/workflows/list_missing.yml b/.github/workflows/list_missing.yml new file mode 100644 index 0000000..48cbe03 --- /dev/null +++ b/.github/workflows/list_missing.yml @@ -0,0 +1,9 @@ +on: [push, pull_request] + +jobs: + pdf: + name: List missing + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh --missing diff --git a/.github/workflows/test_all.yml b/.github/workflows/test_all.yml new file mode 100644 index 0000000..eddc002 --- /dev/null +++ b/.github/workflows/test_all.yml @@ -0,0 +1,14 @@ +on: + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test all (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh diff --git a/.github/workflows/test_datastructures.yml b/.github/workflows/test_datastructures.yml new file mode 100644 index 0000000..9e58389 --- /dev/null +++ b/.github/workflows/test_datastructures.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/datastructures/**' + - 'test/datastructures/**' + pull_request: + paths: + - 'content/datastructures/**' + - 'test/datastructures/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test datastructures (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh datastructures diff --git a/.github/workflows/test_geometry.yml b/.github/workflows/test_geometry.yml new file mode 100644 index 0000000..c1cc95d --- /dev/null +++ b/.github/workflows/test_geometry.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/geometry/**' + - 'test/geometry/**' + pull_request: + paths: + - 'content/geometry/**' + - 'test/geometry/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test geometry (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh geometry diff --git a/.github/workflows/test_graph.yml b/.github/workflows/test_graph.yml new file mode 100644 index 0000000..b402d21 --- /dev/null +++ b/.github/workflows/test_graph.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/graph/**' + - 'test/graph/**' + pull_request: + paths: + - 'content/graph/**' + - 'test/graph/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test graph (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh graph diff --git a/.github/workflows/test_math.yml b/.github/workflows/test_math.yml new file mode 100644 index 0000000..6df75db --- /dev/null +++ b/.github/workflows/test_math.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/math/**' + - 'test/math/**' + pull_request: + paths: + - 'content/math/**' + - 'test/math/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test math (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh math diff --git a/.github/workflows/test_other.yml b/.github/workflows/test_other.yml new file mode 100644 index 0000000..07592d5 --- /dev/null +++ b/.github/workflows/test_other.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/other/**' + - 'test/other/**' + pull_request: + paths: + - 'content/other/**' + - 'test/other/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test other (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh other diff --git a/.github/workflows/test_pdf.yml b/.github/workflows/test_pdf.yml new file mode 100644 index 0000000..e1d660b --- /dev/null +++ b/.github/workflows/test_pdf.yml @@ -0,0 +1,25 @@ +on: + push: + paths: + - 'content/**' + - 'Makefile' + pull_request: + paths: + - 'content/**' + - 'Makefile' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test pdf (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt-get update + sudo apt-get install latexmk texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-lang-german texlive-fonts-extra + - run: make diff --git a/.github/workflows/test_string.yml b/.github/workflows/test_string.yml new file mode 100644 index 0000000..8ca5e1b --- /dev/null +++ b/.github/workflows/test_string.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/string/**' + - 'test/string/**' + pull_request: + paths: + - 'content/string/**' + - 'test/string/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test string (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh string diff --git a/.github/workflows/test_template.yml b/.github/workflows/test_template.yml new file mode 100644 index 0000000..827b9ac --- /dev/null +++ b/.github/workflows/test_template.yml @@ -0,0 +1,22 @@ +on: + push: + paths: + - 'content/template/**' + - 'test/template/**' + pull_request: + paths: + - 'content/template/**' + - 'test/template/**' + workflow_dispatch: + +jobs: + pdf: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-22.04] + name: Test template (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - run: ./test/test.sh template @@ -220,3 +220,8 @@ TSWLatexianTemp* *-tags.tex *~ + +# ignore build dir +build/* +# dont ignore build tcr +!tcr.pdf @@ -1,5 +1,4 @@ all: - latexmk -pdf tcr + cd content; latexmk -pdf tcr -output-directory=.. -aux-directory=../build/ -usepretex="\newcommand{\gitorigin}{https://github.com/mzuenni/ContestReference/tree/$(shell git branch --show-current)/content/}" clean: - latexmk -c tcr - rm -f *.thm + rm -r build/* diff --git a/datastructures/LCT.cpp b/content/datastructures/LCT.cpp index c1dd278..c1dd278 100644 --- a/datastructures/LCT.cpp +++ b/content/datastructures/LCT.cpp diff --git a/datastructures/bitset.cpp b/content/datastructures/bitset.cpp index a89746c..d19abb0 100644 --- a/datastructures/bitset.cpp +++ b/content/datastructures/bitset.cpp @@ -4,4 +4,4 @@ bits._Find_next(2); //4 bits._Find_next(4); //10 bzw. N bits[x] = 1; //not bits.set(x) or bits.reset(x)! bits[x].flip(); //not bits.flip(x)! -bits.count() //number of set bits +bits.count(); //number of set bits diff --git a/datastructures/datastructures.tex b/content/datastructures/datastructures.tex index d35dfb0..40132a9 100644 --- a/datastructures/datastructures.tex +++ b/content/datastructures/datastructures.tex @@ -3,22 +3,22 @@ \begin{algorithm}{Segmentbaum} \begin{methods} \method{SegTree}{baut den Baum auf}{n} - \method{query}{findet Summe über [l, r)}{\log(n)} + \method{query}{findet Summe über $[l, r)$}{\log(n)} \method{update}{ändert einen Wert}{\log(n)} \end{methods} \sourcecode{datastructures/segmentTree.cpp} \subsubsection{Lazy Propagation} Assignment modifications, sum queries \\ - \method{lower\_bound}{erster Index in [l, r) $\geq$ x (erfordert max-combine)}{\log(n)} + \method{lower\_bound}{erster Index in $[l, r)$ $\geq$ x (erfordert max-combine)}{\log(n)} \sourcecode{datastructures/lazyPropagation.cpp} \end{algorithm} \begin{algorithm}{Wavelet Tree} \begin{methods} - \method{Constructor}{baut den Baum auf}{n\*\log(n)} - \method{kth}{sort $[l, r)[k]$}{\log(n)} - \method{countSmaller}{Anzahl elemente in $[l, r)$ kleiner als $k$}{\log(n)} + \method{WaveletTree}{baut den Baum auf}{n\*\log(\Sigma)} + \method{kth}{sort $[l, r)[k]$}{\log(\Sigma)} + \method{countSmaller}{Anzahl elemente in $[l, r)$ kleiner als $k$}{\log(\Sigma)} \end{methods} \sourcecode{datastructures/waveletTree.cpp} \end{algorithm} @@ -27,15 +27,15 @@ \begin{algorithm}{Fenwick Tree} \begin{methods} \method{init}{baut den Baum auf}{n\*\log(n)} - \method{prefix\_sum}{summe von [0, i]}{\log(n)} + \method{prefix\_sum}{summe von $[0, i]$}{\log(n)} \method{update}{addiert ein Delta zu einem Element}{\log(n)} \end{methods} \sourcecode{datastructures/fenwickTree.cpp} \begin{methods} \method{init}{baut den Baum auf}{n\*\log(n)} - \method{prefix\_sum}{summe von [0, i]}{\log(n)} - \method{update}{addiert ein Delta zu allen Elementen [l, r)}{\log(n)} + \method{prefix\_sum}{summe von [$0, i]$}{\log(n)} + \method{update}{addiert ein Delta zu allen Elementen $[l, r)$. $l\leq r$!}{\log(n)} \end{methods} \sourcecode{datastructures/fenwickTree2.cpp} \end{algorithm} @@ -47,7 +47,7 @@ \begin{algorithm}{(Implicit) Treap (Cartesian Tree)} \begin{methods} - \method{insert}{fügt wert $\mathit{val}$ an stelle $i$ ein (verschiebt alle Positionen >= $i$)}{\log(n)} + \method{insert}{fügt wert $\mathit{val}$ an stelle $i$ ein (verschiebt alle Positionen $\geq i$)}{\log(n)} \method{remove}{löscht werte $[i,i+\mathit{count})$}{\log(n)} \end{methods} \sourcecode{datastructures/treap2.cpp} @@ -56,7 +56,7 @@ \begin{algorithm}{Range Minimum Query} \begin{methods} \method{init}{baut Struktur auf}{n\*\log(n)} - \method{queryIdempotent}{Index des Minimums in [l, r)}{1} + \method{queryIdempotent}{Index des Minimums in $[l, r)$. $l<r$!}{1} \end{methods} \begin{itemize} \item \code{better}-Funktion muss idempotent sein! @@ -70,7 +70,7 @@ \begin{algorithm}{Link-Cut-Tree} \begin{methods} - \method{Constructor}{baut Wald auf}{n} + \method{LCT}{baut Wald auf}{n} \method{connected}{prüft ob zwei Knoten im selben Baum liegen}{\log(n)} \method{link}{fügt $\{x,y\}$ Kante ein}{\log(n)} \method{cut}{entfernt $\{x,y\}$ Kante}{\log(n)} @@ -119,18 +119,3 @@ \sourcecode{datastructures/persistent.cpp} \sourcecode{datastructures/persistentArray.cpp} \end{algorithm} - -\begin{algorithm}[optional]{Range Minimum Query} - \begin{methods} - \method{init}{baut Struktur auf}{n\*\log(n)} - \method{query}{Index des Minimums in [l, r)}{1} - \end{methods} - \sourcecode{datastructures/RMQ.cpp} -\end{algorithm} - -\begin{algorithm}[optional]{Erste unbenutzte natürliche Zahl} - \begin{methods} - \method{get\_first\_unused}{findet kleinste unbenutzte Zahl}{\log(n)} - \end{methods} - \sourcecode{datastructures/firstUnused.cpp} -\end{algorithm} diff --git a/datastructures/dynamicConvexHull.cpp b/content/datastructures/dynamicConvexHull.cpp index d669847..d669847 100644 --- a/datastructures/dynamicConvexHull.cpp +++ b/content/datastructures/dynamicConvexHull.cpp diff --git a/content/datastructures/fenwickTree.cpp b/content/datastructures/fenwickTree.cpp new file mode 100644 index 0000000..eb5cd73 --- /dev/null +++ b/content/datastructures/fenwickTree.cpp @@ -0,0 +1,15 @@ +vector<ll> tree; + +void update(int i, ll val) { + for (i++; i < sz(tree); i += i & -i) tree[i] += val; +} + +void init(int n) { + tree.assign(n + 1, 0); +} + +ll prefix_sum(int i) { + ll sum = 0; + for (i++; i > 0; i -= i & -i) sum += tree[i]; + return sum; +} diff --git a/datastructures/fenwickTree2.cpp b/content/datastructures/fenwickTree2.cpp index ff87e2e..9384e3c 100644 --- a/datastructures/fenwickTree2.cpp +++ b/content/datastructures/fenwickTree2.cpp @@ -1,21 +1,21 @@ vector<ll> add, mul; void update(int l, int r, ll val) { - for (int tl = l + 1; tl < sz(add); tl += tl&(-tl)) + for (int tl = l + 1; tl < sz(add); tl += tl & -tl) add[tl] += val, mul[tl] -= val * l; - for (int tr = r + 1; tr < sz(add); tr += tr&(-tr)) + for (int tr = r + 1; tr < sz(add); tr += tr & -tr) add[tr] -= val, mul[tr] += val * r; } void init(vector<ll>& v) { - mul.assign(sz(v) + 1,0); - add.assign(sz(v) + 1,0); + mul.assign(sz(v) + 1, 0); + add.assign(sz(v) + 1, 0); for(int i = 0; i < sz(v); i++) update(i, i + 1, v[i]); } -ll prefix_sum (int i) { +ll prefix_sum(int i) { ll res = 0; i++; - for (int ti = i; ti > 0; ti -= ti&(-ti)) + for (int ti = i; ti > 0; ti -= ti & -ti) res += add[ti] * i + mul[ti]; return res; } diff --git a/datastructures/lazyPropagation.cpp b/content/datastructures/lazyPropagation.cpp index 0fe7bbe..441590e 100644 --- a/datastructures/lazyPropagation.cpp +++ b/content/datastructures/lazyPropagation.cpp @@ -1,10 +1,12 @@ struct SegTree { using T = ll; using U = ll; - int n, h; + int n; static constexpr T E = 0; // Neutral element for combine - static constexpr U UF = 0; // Unused value by updates - vector<T> tree; vector<U> lazy; - vector<ll> k; // size of segments (optional) + static constexpr U UF = inf; // Unused value by updates + vector<T> tree; + int h; + vector<U> lazy; + vector<int> k; // size of segments (optional) SegTree(const vector<T>& a) : n(sz(a) + 1), tree(2 * n, E), //SegTree(int size, T def = E) : n(size + 1), tree(2 * n, def), @@ -62,7 +64,7 @@ struct SegTree { } // Optional: - ll lower_bound(int l, int r, T x) { + int lower_bound(int l, int r, T x) { l += n, r += n; push(l), push(r - 1); int a[64] = {}, lp = 0, rp = 64; diff --git a/datastructures/lichao.cpp b/content/datastructures/lichao.cpp index f66778e..f66778e 100644 --- a/datastructures/lichao.cpp +++ b/content/datastructures/lichao.cpp diff --git a/datastructures/monotonicConvexHull.cpp b/content/datastructures/monotonicConvexHull.cpp index 44bff83..44bff83 100644 --- a/datastructures/monotonicConvexHull.cpp +++ b/content/datastructures/monotonicConvexHull.cpp diff --git a/datastructures/pbds.cpp b/content/datastructures/pbds.cpp index c2b44cc..f0889a2 100644 --- a/datastructures/pbds.cpp +++ b/content/datastructures/pbds.cpp @@ -8,7 +8,7 @@ using Tree = tree<T, null_type, less<T>, rb_tree_tag, template<typename T> struct chash { - const uint64_t C = ll(2e18 * acos(-1)) | 199; // random odd + static const uint64_t C = ll(2e18 * acos(-1)) | 199; // random odd size_t operator()(T o) const { return __builtin_bswap64(hash<T>()(o) * C); }}; diff --git a/datastructures/persistent.cpp b/content/datastructures/persistent.cpp index 0a65a79..4093cdc 100644 --- a/datastructures/persistent.cpp +++ b/content/datastructures/persistent.cpp @@ -7,7 +7,7 @@ struct persistent { : time(time), data(1, {time, value}) {}
T get(int t) {
- return prev(upper_bound(all(data), {t+1, {}}))->second;
+ return prev(upper_bound(all(data), pair{t+1, T{}}))->second;
}
int set(T value) {
diff --git a/datastructures/persistentArray.cpp b/content/datastructures/persistentArray.cpp index 60d8b17..60d8b17 100644 --- a/datastructures/persistentArray.cpp +++ b/content/datastructures/persistentArray.cpp diff --git a/datastructures/segmentTree.cpp b/content/datastructures/segmentTree.cpp index 79c6cae..6b69d0b 100644 --- a/datastructures/segmentTree.cpp +++ b/content/datastructures/segmentTree.cpp @@ -11,7 +11,7 @@ struct SegTree { tree[i] = comb(tree[2 * i], tree[2 * i + 1]); }} - ll comb(T a, T b) {return a + b;} // modify this + neutral + T comb(T a, T b) {return a + b;} // modify this + neutral void update(int i, T val) { tree[i += n] = val; // apply update code diff --git a/datastructures/sparseTable.cpp b/content/datastructures/sparseTable.cpp index 63cce48..b3f946e 100644 --- a/datastructures/sparseTable.cpp +++ b/content/datastructures/sparseTable.cpp @@ -6,7 +6,7 @@ struct SparseTable { return a[lidx] <= a[ridx] ? lidx : ridx; } - void init(vector<ll> *vec) { + void init(vector<ll>* vec) { int n = sz(*vec); a = vec->data(); st.assign(__lg(n) + 1, vector<int>(n)); @@ -17,6 +17,7 @@ struct SparseTable { }}} int queryIdempotent(int l, int r) { + if (r <= l) return -1; int j = __lg(r - l); //31 - builtin_clz(r - l); return better(st[j][l] , st[j][r - (1 << j)]); } diff --git a/datastructures/sparseTableDisjoint.cpp b/content/datastructures/sparseTableDisjoint.cpp index 31e9025..55165d4 100644 --- a/datastructures/sparseTableDisjoint.cpp +++ b/content/datastructures/sparseTableDisjoint.cpp @@ -1,5 +1,5 @@ struct DisjointST { - static constexpr ll neutral = 0 + static constexpr ll neutral = 0; vector<vector<ll>> dst; ll* a; @@ -7,7 +7,7 @@ struct DisjointST { return x + y; } - void init(vector<ll> *vec) { + void init(vector<ll>* vec) { int n = sz(*vec); a = vec->data(); dst.assign(__lg(n) + 1, vector<ll>(n + 1, neutral)); @@ -20,6 +20,7 @@ struct DisjointST { }}} ll query(int l, int r) { + if (r <= l) return neutral; int h = __lg(l ^ r); return combine(dst[h][l], dst[h][r]); } diff --git a/datastructures/stlHashMap.cpp b/content/datastructures/stlHashMap.cpp index b107dde..b107dde 100644 --- a/datastructures/stlHashMap.cpp +++ b/content/datastructures/stlHashMap.cpp diff --git a/datastructures/stlPriorityQueue.cpp b/content/datastructures/stlPriorityQueue.cpp index 32b2455..32b2455 100644 --- a/datastructures/stlPriorityQueue.cpp +++ b/content/datastructures/stlPriorityQueue.cpp diff --git a/datastructures/stlRope.cpp b/content/datastructures/stlRope.cpp index 804cd67..804cd67 100644 --- a/datastructures/stlRope.cpp +++ b/content/datastructures/stlRope.cpp diff --git a/datastructures/stlTree.cpp b/content/datastructures/stlTree.cpp index fbb68b9..fbb68b9 100644 --- a/datastructures/stlTree.cpp +++ b/content/datastructures/stlTree.cpp diff --git a/datastructures/treap.cpp b/content/datastructures/treap.cpp index c96e36a..c96e36a 100644 --- a/datastructures/treap.cpp +++ b/content/datastructures/treap.cpp diff --git a/datastructures/treap2.cpp b/content/datastructures/treap2.cpp index 10168ca..c5a60e9 100644 --- a/datastructures/treap2.cpp +++ b/content/datastructures/treap2.cpp @@ -3,7 +3,7 @@ struct Treap { struct Node { ll val; int prio, size = 1, l = -1, r = -1; - Node (ll x) : val(x), prio(rng()) {} + Node(ll x) : val(x), prio(rng()) {} }; vector<Node> treap; @@ -15,35 +15,35 @@ struct Treap { void upd(int v) { if (v < 0) return; - auto *V = &treap[v]; - V->size = 1 + getSize(V->l) + getSize(V->r); + auto& V = treap[v]; + V.size = 1 + getSize(V.l) + getSize(V.r); // Update Node Code } void push(int v) { if (v < 0) return; - //auto *V = &treap[v]; - //if (V->lazy) { + //auto& V = treap[v]; + //if (V.lazy) { // Lazy Propagation Code - // if (V->l >= 0) treap[V->l].lazy = true; - // if (V->r >= 0) treap[V->r].lazy = true; - // V->lazy = false; + // if (V.l >= 0) treap[V.l].lazy = true; + // if (V.r >= 0) treap[V.r].lazy = true; + // V.lazy = false; //} } pair<int, int> split(int v, int k) { if (v < 0) return {-1, -1}; - auto *V = &treap[v]; + auto& V = treap[v]; push(v); - if (getSize(V->l) >= k) { // "V->val >= k" for lower_bound(k) - auto [left, right] = split(V->l, k); - V->l = right; + if (getSize(V.l) >= k) { // "V.val >= k" for lower_bound(k) + auto [left, right] = split(V.l, k); + V.l = right; upd(v); return {left, v}; } else { // and only "k" - auto [left, right] = split(V->r, k - getSize(V->l) - 1); - V->r = left; + auto [left, right] = split(V.r, k - getSize(V.l) - 1); + V.r = left; upd(v); return {v, right}; }} diff --git a/datastructures/unionFind.cpp b/content/datastructures/unionFind.cpp index 68eef86..dd5a569 100644 --- a/datastructures/unionFind.cpp +++ b/content/datastructures/unionFind.cpp @@ -6,9 +6,9 @@ void init(int n) { //Initialisieren unions.assign(n, -1); } -int findSet(int n) { // Pfadkompression - if (unions[n] < 0) return n; - return unions[n] = findSet(unions[n]); +int findSet(int a) { // Pfadkompression + if (unions[a] < 0) return a; + return unions[a] = findSet(unions[a]); } void linkSets(int a, int b) { // Union by size. diff --git a/datastructures/waveletTree.cpp b/content/datastructures/waveletTree.cpp index 476658e..090cdb2 100644 --- a/datastructures/waveletTree.cpp +++ b/content/datastructures/waveletTree.cpp @@ -21,7 +21,7 @@ struct WaveletTree { // kth element in sort[l, r) all 0-indexed ll kth(int l, int r, int k) { - if (l >= r || k >= r - l) return -1; + if (k < 0 || l + k >= r) return -1; if (lo + 1 >= hi) return lo; int inLeft = b[r] - b[l]; if (k < inLeft) return ln->kth(b[l], b[r], k); diff --git a/geometry/antipodalPoints.cpp b/content/geometry/antipodalPoints.cpp index 110cc74..110cc74 100644 --- a/geometry/antipodalPoints.cpp +++ b/content/geometry/antipodalPoints.cpp diff --git a/geometry/circle.cpp b/content/geometry/circle.cpp index 8ebc800..6789c52 100644 --- a/geometry/circle.cpp +++ b/content/geometry/circle.cpp @@ -1,7 +1,7 @@ -// berechnet die Schnittpunkte von zwei kreisen +// berechnet die Schnittpunkte von zwei Kreisen // (Kreise dürfen nicht gleich sein!) vector<pt> circleIntersection(pt c1, double r1, - pt c2, double r2) { + pt c2, double r2) { double d = abs(c1 - c2); if (d < abs(r1 - r2) || d > abs(r1 + r2)) return {}; double a = (r1 * r1 - r2 * r2 + d * d) / (2 * d); @@ -13,16 +13,16 @@ vector<pt> circleIntersection(pt c1, double r1, } // berechnet die Schnittpunkte zwischen -// einem Kreis(Kugel) und einer Grade 2d und 3d +// einem Kreis(Kugel) und einem Strahl (2D und 3D) vector<pt> circleRayIntersection(pt center, double r, - pt orig, pt dir) { + pt orig, pt dir) { vector<pt> result; - double a = dot(dir, dir); + double a = norm(dir); double b = 2 * dot(dir, orig - center); - double c = dot(orig - center, orig - center) - r * r; + double c = norm(orig - center) - r * r; double discr = b * b - 4 * a * c; if (discr >= 0) { - //t in [0, 1] => schnitt mit segment [orig, orig + dir] + //t in [0, 1] => schnitt mit Segment [orig, orig + dir] double t1 = -(b + sqrt(discr)) / (2 * a); double t2 = -(b - sqrt(discr)) / (2 * a); if (t1 >= 0) result.push_back(t1 * dir + orig); diff --git a/content/geometry/closestPair.cpp b/content/geometry/closestPair.cpp new file mode 100644 index 0000000..9b115f3 --- /dev/null +++ b/content/geometry/closestPair.cpp @@ -0,0 +1,27 @@ +ll rec(vector<pt>::iterator a, int l, int r) { + if (r - l < 2) return INF; + int m = (l + r) / 2; + ll midx = a[m].real(); + ll ans = min(rec(a, l, m), rec(a, m, r)); + + inplace_merge(a+l, a+m, a+r, [](const pt& x, const pt& y) { + return x.imag() < y.imag(); + }); + + pt tmp[8]; + fill(all(tmp), a[l]); + for (int i = l + 1, next = 0; i < r; i++) { + if (ll x = a[i].real() - midx; x * x < ans) { + for (pt& p : tmp) ans = min(ans, norm(p - a[i])); + tmp[next++ & 7] = a[i]; + } + } + return ans; +} + +ll shortestDist(vector<pt> a) { // sz(pts) > 1 + sort(all(a), [](const pt& x, const pt& y) { + return x.real() < y.real(); + }); + return rec(a.begin(), 0, sz(a)); +} diff --git a/geometry/convexHull.cpp b/content/geometry/convexHull.cpp index b1de170..6d89e05 100644 --- a/geometry/convexHull.cpp +++ b/content/geometry/convexHull.cpp @@ -6,14 +6,13 @@ vector<pt> convexHull(vector<pt> pts){ pts.erase(unique(all(pts)), pts.end()); int k = 0; vector<pt> h(2 * sz(pts)); - for (int i = 0; i < sz(pts); i++) {// Untere Hülle. - while (k > 1 && cross(h[k-2], h[k-1], pts[i]) <= 0) k--; - h[k++] = pts[i]; - } - for (int i = sz(pts)-2, t = k; i >= 0; i--) {// Obere Hülle. - while (k > t && cross(h[k-2], h[k-1], pts[i]) <= 0) k--; - h[k++] = pts[i]; - } + 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--; + 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; } diff --git a/geometry/delaunay.cpp b/content/geometry/delaunay.cpp index 1008b39..c813892 100644 --- a/geometry/delaunay.cpp +++ b/content/geometry/delaunay.cpp @@ -1,9 +1,9 @@ using lll = __int128; -using pt = complex<ll>; +using pt = complex<lll>; -constexpr pt INF_PT = pt(1e18, 1e18); +constexpr pt INF_PT = pt(2e18, 2e18); -bool circ(pt p, pt a, pt b, pt c) {// p in circle(A,B,C) +bool circ(pt p, pt a, pt b, pt c) {// p in circle(A,B,C), ABC must be ccw return imag((c-b)*conj(p-c)*(a-p)*conj(b-a)) < 0; } @@ -21,7 +21,7 @@ struct QuadEdge { deque<QuadEdge> edgeData; QuadEdge* makeEdge(pt from, pt to) { - for (int j : {0,1,2,3}) edgeData.push_back({}); + for (int _ : {0,1,2,3}) edgeData.push_back({}); auto e = edgeData.end() - 4; for (int j : {0,1,2,3}) e[j].onext = e[j^3].rot = &e[j^(j>>1)]; e[0].orig = from; @@ -65,7 +65,7 @@ pair<QuadEdge*, QuadEdge*> rec(IT l, IT r) { if (n == 2) return {a, a->rev()}; QuadEdge* b = makeEdge(l[1], l[2]); splice(a->rev(), b); - int side = cross(l[0], l[1], l[2]); + auto side = cross(l[0], l[1], l[2]); QuadEdge* c = nullptr; if (side != 0) c = connect(b, a); if (side >= 0) return {a, b->rev()}; diff --git a/geometry/formulars.cpp b/content/geometry/formulas.cpp index 22e9e32..5d4e10d 100644 --- a/geometry/formulars.cpp +++ b/content/geometry/formulas.cpp @@ -12,20 +12,20 @@ double angle(pt a) {return arg(a);} pt rotate(pt a, double theta) {return a * polar(1.0, theta);} // Skalarprodukt. -double dot(pt a, pt b) {return real(conj(a) * b);} +auto dot(pt a, pt b) {return real(conj(a) * b);} // abs()^2.(pre c++20) -double norm(pt a) {return dot(a, a);} +auto norm(pt a) {return dot(a, a);} // Kreuzprodukt, 0, falls kollinear. -double cross(pt a, pt b) {return imag(conj(a) * b);} -double cross(pt p, pt a, pt b) {return cross(a - p, b - p);} +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);} // 1 => c links von a->b // 0 => a, b und c kolliniear // -1 => c rechts von a->b -int orientation(pt a, pt b, pt c) { - double orien = cross(b - a, c - a); +int ccw(pt a, pt b, pt c) { + auto orien = cross(b - a, c - a); return (orien > EPS) - (orien < -EPS); } diff --git a/geometry/formulars3d.cpp b/content/geometry/formulas3d.cpp index 84e17c0..dee3ce8 100644 --- a/geometry/formulars3d.cpp +++ b/content/geometry/formulas3d.cpp @@ -1,8 +1,8 @@ // Skalarprodukt -double operator|(pt3 a, pt3 b) { +auto operator|(pt3 a, pt3 b) { return a.x * b.x + a.y*b.y + a.z*b.z; } -double dot(pt3 a, pt3 b) {return a|b;} +auto dot(pt3 a, pt3 b) {return a|b;} // Kreuzprodukt pt3 operator*(pt3 a, pt3 b) {return {a.y*b.z - a.z*b.y, @@ -15,14 +15,14 @@ double abs(pt3 a) {return sqrt(dot(a, a));} double abs(pt3 a, pt3 b) {return abs(b - a);} // Mixedprodukt -double mixed(pt3 a, pt3 b, pt3 c) {return a*b|c;}; +auto mixed(pt3 a, pt3 b, pt3 c) {return a*b|c;}; // orientierung von p zu der Ebene durch a, b, c // -1 => gegen den Uhrzeigersinn, // 0 => kolliniear, // 1 => im Uhrzeigersinn. -int orientation(pt3 a, pt3 b, pt3 c, pt3 p) { - double orien = mixed(b - a, c - a, p - a); +int ccw(pt3 a, pt3 b, pt3 c, pt3 p) { + auto orien = mixed(b - a, c - a, p - a); return (orien > EPS) - (orien < -EPS); } @@ -34,7 +34,7 @@ double distToPlane(pt3 a, pt3 b, pt3 c, pt3 p) { // Liegt p in der Ebene a,b,c? bool pointOnPlane(pt3 a, pt3 b, pt3 c, pt3 p) { - return orientation(a, b, c, p) == 0; + return ccw(a, b, c, p) == 0; } // Schnittpunkt von der Grade a-b und der Ebene c,d,e diff --git a/geometry/geometry.tex b/content/geometry/geometry.tex index 95c0adb..92285c4 100644 --- a/geometry/geometry.tex +++ b/content/geometry/geometry.tex @@ -7,28 +7,28 @@ \sourcecode{geometry/closestPair.cpp} \end{algorithm} -\begin{algorithm}{Rotating calipers} - \begin{methods} - \method{antipodalPoints}{berechnet antipodale Punkte}{n} - \end{methods} - \textbf{WICHTIG:} Punkte müssen gegen den Uhrzeigersinn sortiert sein und konvexes Polygon bilden! - \sourcecode{geometry/antipodalPoints.cpp} -\end{algorithm} - \begin{algorithm}{Konvexehülle} \begin{methods} \method{convexHull}{berechnet konvexe Hülle}{n\*\log(n)} \end{methods} \begin{itemize} - \item Konvexe Hülle gegen den Uhrzeigersinn sortiert + \item konvexe Hülle gegen den Uhrzeigersinn sortiert \item nur Eckpunkte enthalten(für alle Punkte = im CCW Test entfernen) \item erster und letzter Punkt sind identisch \end{itemize} \sourcecode{geometry/convexHull.cpp} \end{algorithm} +\begin{algorithm}{Rotating calipers} + \begin{methods} + \method{antipodalPoints}{berechnet antipodale Punkte}{n} + \end{methods} + \textbf{WICHTIG:} Punkte müssen gegen den Uhrzeigersinn sortiert sein und konvexes Polygon bilden! + \sourcecode{geometry/antipodalPoints.cpp} +\end{algorithm} + \subsection{Formeln~~--~\texttt{std::complex}} -\sourcecode{geometry/formulars.cpp} +\sourcecode{geometry/formulas.cpp} \sourcecode{geometry/linesAndSegments.cpp} \sourcecode{geometry/sortAround.cpp} \input{geometry/triangle} @@ -36,8 +36,8 @@ \sourcecode{geometry/polygon.cpp} \sourcecode{geometry/circle.cpp} -\subsection{Formeln - 3D} -\sourcecode{geometry/formulars3d.cpp} +\subsection{Formeln -- 3D} +\sourcecode{geometry/formulas3d.cpp} \optional{ \subsection{3D-Kugeln} diff --git a/geometry/hpi.cpp b/content/geometry/hpi.cpp index 3509e0e..3509e0e 100644 --- a/geometry/hpi.cpp +++ b/content/geometry/hpi.cpp diff --git a/geometry/lines.cpp b/content/geometry/lines.cpp index 95536a4..95536a4 100644 --- a/geometry/lines.cpp +++ b/content/geometry/lines.cpp diff --git a/content/geometry/linesAndSegments.cpp b/content/geometry/linesAndSegments.cpp new file mode 100644 index 0000000..1e21cba --- /dev/null +++ b/content/geometry/linesAndSegments.cpp @@ -0,0 +1,89 @@ +// Liegt p auf der Geraden a-b? 2d und 3d +bool pointOnLine(pt a, pt b, pt p) { + return ccw(a, b, p) == 0; +} + +// Test auf Linienschnitt zwischen a-b und c-d. (nicht identisch) +bool lineIntersection(pt a, pt b, pt c, pt d) { + return abs(cross(a - b, c - d)) < EPS; +} + +// Berechnet den Schnittpunkt der Graden a-b und c-d. +// die Graden dürfen nicht parallel sein! +pt lineIntersection2(pt a, pt b, pt c, pt d) { + double x = cross(b - a, d - c); + double y = cross(c - a, d - c); + return a + y/x*(b - a); +} + +// 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); +} + +// Projiziert p auf die Gerade a-b +pt projectToLine(pt a, pt b, pt p) { + return a + (b - a) * dot(p - a, b - a) / norm(b - a); +} + +// sortiert alle Punkte pts auf einer Linie entsprechend dir +void sortLine(pt dir, vector<pt>& pts) { // (2d und 3d) + sort(all(pts), [&](pt a, pt b){ + return dot(dir, a) < dot(dir, b); + }); +} + +// Liegt p auf der Strecke a-b? (nutze < für inberhalb) +bool pointOnSegment(pt a, pt b, pt p) { + if (ccw(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 distToLine(a, b, p); +} + +// Test auf Streckenschnitt zwischen a-b und c-d. +bool segmentIntersection(pt a, pt b, pt c, pt d) { + if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) + return pointOnSegment(a,b,c) || + pointOnSegment(a,b,d) || + pointOnSegment(c,d,a) || + pointOnSegment(c,d,b); + return ccw(a, b, c) * ccw(a, b, d) <= 0 && + ccw(c, d, a) * ccw(c, d, b) <= 0; +} + +// Berechnet die Schnittpunkte der Strecken a-b und c-d. +// Enthält entweder keinen Punkt, den einzigen Schnittpunkt +// oder die Endpunkte der Schnittstrecke. +vector<pt> segmentIntersection2(pt a, pt b, pt c, pt d) { + double x = cross(b - a, d - c); + double y = cross(c - a, d - c); + double z = cross(b - a, a - c); + if (x < 0) {x = -x; y = -y; z = -z;} + if (y < -EPS || y-x > EPS || z < -EPS || z-x > EPS) return {}; + if (x > EPS) return {a + y/x*(b - a)}; + vector<pt> result; + auto insertUnique = [&](pt p) { + for (auto q : result) if (abs(p - q) < EPS) return; + result.push_back(p); + }; + if (dot(c-a, d-a) < EPS) insertUnique(a); + if (dot(c-b, d-b) < EPS) insertUnique(b); + if (dot(a-c, b-c) < EPS) insertUnique(c); + if (dot(a-d, b-d) < EPS) insertUnique(d); + return result; +} + +// Kürzeste Entfernung zwischen den Strecken a-b und c-d. +double distBetweenSegments(pt a, pt b, pt c, pt d) { + if (segmentIntersection(a, b, c, d)) return 0.0; + return min({distToSegment(a, b, c), distToSegment(a, b, d), + distToSegment(c, d, a), distToSegment(c, d, b)}); +} diff --git a/geometry/polygon.cpp b/content/geometry/polygon.cpp index e3ce33e..3178290 100644 --- a/geometry/polygon.cpp +++ b/content/geometry/polygon.cpp @@ -1,13 +1,13 @@ // Flächeninhalt eines Polygons (nicht selbstschneidend). // Punkte gegen den Uhrzeigersinn: positiv, sonst negativ. double area(const vector<pt>& poly) { //poly[0] == poly.back() - double res = 0; + ll res = 0; for (int i = 0; i + 1 < sz(poly); i++) res += cross(poly[i], poly[i + 1]); return 0.5 * res; } -// Anzahl drehungen einer Polyline um einen Punkt +// Anzahl ccw drehungen einer Polyline um einen Punkt // p nicht auf rand und poly[0] == poly.back() // res != 0 or (res & 1) != 0 um inside zu prüfen bei // selbstschneidenden Polygonen (definitions Sache) @@ -18,7 +18,7 @@ ll windingNumber(pt p, const vector<pt>& poly) { if (real(a) > real(b)) swap(a, b); if (real(a) <= real(p) && real(p) < real(b) && cross(p, a, b) < 0) { - res += orientation(p, poly[i], poly[i + 1]); + res += ccw(p, poly[i], poly[i + 1]); }} return res; } @@ -40,7 +40,7 @@ bool inside(pt p, const vector<pt>& poly) { // convex hull without duplicates, h[0] != h.back() // apply comments if border counts as inside -bool inside(pt p, const vector<pt>& hull) { +bool insideConvex(pt p, const vector<pt>& hull) { int l = 0, r = sz(hull) - 1; if (cross(hull[0], hull[r], p) >= 0) return false; // > 0 while (l + 1 < r) { @@ -78,17 +78,17 @@ vector<pt> minkowski(vector<pt> ps, vector<pt> qs) { } // convex hulls without duplicates, h[0] != h.back() -double dist(const vector<pt>& ps, const vector<pt>& qs) { +double dist(const vector<pt>& ps, vector<pt> qs) { for (pt& q : qs) q *= -1; auto p = minkowski(ps, qs); p.push_back(p[0]); - double res = 0.0; - //bool intersect = true; + double res = INF; + bool intersect = true; for (ll i = 0; i + 1 < sz(p); i++) { - //intersect &= cross(p[i], p[i+1] - p[i]) <= 0; - res = max(res, cross(p[i], p[i+1]-p[i]) / abs(p[i+1]-p[i])); + intersect &= cross(p[i], p[i+1]) >= 0; + res = min(res, distToSegment(p[i], p[i+1], 0)); } - return res; + return intersect ? 0 : res; } bool left(pt of, pt p) {return cross(p, of) < 0 || @@ -111,16 +111,16 @@ int extremal(const vector<pt>& hull, pt dir) { if (cross(dir, dm) < 0) l = m; else r = m; }} - return r; + return r % (sz(hull) - 1); } // convex hulls without duplicates, hull[0] == hull.back() and // hull[0] must be a convex point (with angle < pi) // {} if no intersection // {x} if corner is only intersection -// {a, b} segments (a,a+1) and (b,b+1) intersected (if only the -// border is intersected corners a and b are the start and end) -vector<int> intersect(const vector<pt>& hull, pt a, pt b) { +// {i, j} segments (i,i+1) and (j,j+1) intersected (if only the +// border is intersected corners i and j are the start and end) +vector<int> intersectLine(const vector<pt>& hull, pt a, pt b) { int endA = extremal(hull, (a-b) * pt(0, 1)); int endB = extremal(hull, (b-a) * pt(0, 1)); // cross == 0 => line only intersects border @@ -135,7 +135,7 @@ vector<int> intersect(const vector<pt>& hull, pt a, pt b) { while (l + 1 < r) { int m = (l + r) / 2; if (cross(hull[m % n], a, b) <= 0 && - cross(hull[m % n], a, b) != hull(poly[endB], a, b)) + cross(hull[m % n], a, b) != cross(hull[endB], a, b)) l = m; else r = m; } diff --git a/geometry/segmentIntersection.cpp b/content/geometry/segmentIntersection.cpp index 6dc5dc5..4262ddc 100644 --- a/geometry/segmentIntersection.cpp +++ b/content/geometry/segmentIntersection.cpp @@ -3,10 +3,10 @@ struct seg { int id; bool operator<(const seg& o) const { if (real(a) < real(o.a)) { - int s = orientation(a, b, o.a); + int s = ccw(a, b, o.a); return (s > 0 || (s == 0 && imag(a) < imag(o.a))); } else if (real(a) > real(o.a)) { - int s = orientation(o.a, o.b, a); + int s = ccw(o.a, o.b, a); return (s < 0 || (s == 0 && imag(a) < imag(o.a))); } return imag(a) < imag(o.a); diff --git a/geometry/sortAround.cpp b/content/geometry/sortAround.cpp index 86fead7..98d17a8 100644 --- a/geometry/sortAround.cpp +++ b/content/geometry/sortAround.cpp @@ -1,6 +1,7 @@ bool left(pt p) {return real(p) < 0 ||
(real(p) == 0 && imag(p) < 0);}
+// counter clockwise, starting with "11:59"
void sortAround(pt p, vector<pt>& ps) {
sort(all(ps), [&](const pt& a, const pt& b){
if (left(a - p) != left(b - p))
diff --git a/geometry/spheres.cpp b/content/geometry/spheres.cpp index abffde5..ec22262 100644 --- a/geometry/spheres.cpp +++ b/content/geometry/spheres.cpp @@ -1,6 +1,6 @@ -// Great Cirlce Distance mit Längen- und Breitengrad. +// Great Circle Distance mit Längen- und Breitengrad. double gcDist(double pLat, double pLon, - double qLat, double qLon, double radius) { + double qLat, double qLon, double radius) { pLat *= PI / 180; pLon *= PI / 180; qLat *= PI / 180; qLon *= PI / 180; return radius * acos(cos(pLat) * cos(pLon) * @@ -10,7 +10,7 @@ double gcDist(double pLat, double pLon, sin(pLat) * sin(qLat)); } -// Great Cirlce Distance mit kartesischen Koordinaten. +// Great Circle Distance mit kartesischen Koordinaten. double gcDist(point p, point q) { return acos(p.x * q.x + p.y * q.y + p.z * q.z); } diff --git a/geometry/triangle.cpp b/content/geometry/triangle.cpp index 33a8394..534bb10 100644 --- a/geometry/triangle.cpp +++ b/content/geometry/triangle.cpp @@ -3,12 +3,12 @@ pt centroid(pt a, pt b, pt c) {return (a + b + c) / 3.0;} // Flächeninhalt eines Dreicks bei bekannten Eckpunkten. double area(pt a, pt b, pt c) { - return abs(cross(b - a, c - a)) / 2.0; + return abs(cross(a, b, c)) / 2.0; } // Flächeninhalt eines Dreiecks bei bekannten Seitenlängen. double area(double a, double b, double c) { - double s = (a + b + c) / 2.0; + double s = (a + b + c) / 2.0; //unpräzise return sqrt(s * (s-a) * (s-b) * (s-c)); } @@ -27,11 +27,11 @@ pt circumCenter(pt a, pt b, pt c) { return a + d / cross(b, c) / 2.0; } -// 1 => p außerhalb Kreis durch a,b,c +// -1 => p außerhalb Kreis durch a,b,c // 0 => p auf Kreis durch a,b,c -// -1 => p im Kreis durch a,b,c +// 1 => p im Kreis durch a,b,c int insideOutCenter(pt a, pt b, pt c, pt p) {// braucht lll - return sgn(imag((c-b)*conj(p-c)*(a-p)*conj(b-a))); + return ccw(a,b,c) * sgn(imag((c-b)*conj(p-c)*(a-p)*conj(b-a))); } // Sind die Dreiecke a1, b1, c1, and a2, b2, c2 ähnlich? diff --git a/geometry/triangle.tex b/content/geometry/triangle.tex index 3decd54..3decd54 100644 --- a/geometry/triangle.tex +++ b/content/geometry/triangle.tex diff --git a/graph/2sat.cpp b/content/graph/2sat.cpp index 75e54e6..75e54e6 100644 --- a/graph/2sat.cpp +++ b/content/graph/2sat.cpp diff --git a/graph/LCA_sparse.cpp b/content/graph/LCA_sparse.cpp index 649e697..221b5ed 100644 --- a/graph/LCA_sparse.cpp +++ b/content/graph/LCA_sparse.cpp @@ -13,13 +13,13 @@ struct LCA { st.init(&depth); } - void dfs(vector<vector<int>>& adj, int v, ll d=0, int p=-1) { + void dfs(vector<vector<int>>& adj, int v, ll d=0) { visited[idx] = v, depth[idx] = d; first[v] = min(idx, first[v]), idx++; for (int u : adj[v]) { if (first[u] == 2 * sz(adj)) { - dfs(adj, u, d + 1, v); + dfs(adj, u, d + 1); visited[idx] = v, depth[idx] = d, idx++; }}} diff --git a/graph/TSP.cpp b/content/graph/TSP.cpp index cfb1b4d..6223858 100644 --- a/graph/TSP.cpp +++ b/content/graph/TSP.cpp @@ -1,6 +1,6 @@ vector<vector<ll>> dist; // Entfernung zwischen je zwei Punkten. -void TSP() { +auto TSP() { int n = sz(dist), m = 1 << n; vector<vector<edge>> dp(n, vector<edge>(m, edge{INF, -1})); @@ -19,10 +19,11 @@ void TSP() { }}}}} // return dp[0][1]; // Länge der Tour - vector<int> tour; tour.push_back(0); int v = 0; + vector<int> tour = {0}; + int v = 0; while (tour.back() != 0 || sz(tour) == 1) tour.push_back(dp[tour.back()] [(v |= (1 << tour.back()))].to); // Enthält Knoten 0 zweimal. An erster und letzter Position. - // return tour; + return tour; } diff --git a/graph/articulationPoints.cpp b/content/graph/articulationPoints.cpp index 6819bf3..25ff67e 100644 --- a/graph/articulationPoints.cpp +++ b/content/graph/articulationPoints.cpp @@ -8,8 +8,8 @@ vector<vector<Edge>> bcc; int dfs(int v, int from = -1) { int me = num[v] = ++counter, top = me; for (Edge& e : adj[v]) { - if (e.id == from){} - else if (num[e.to]) { + if (e.id == from) continue; + if (num[e.to]) { top = min(top, num[e.to]); if (num[e.to] < me) st.push_back(e); } else { @@ -21,11 +21,9 @@ int dfs(int v, int from = -1) { if (up > me) bridges.push_back(e); if (up <= me) st.push_back(e); if (up == me) { - bcc.emplace_back(); - while (sz(st) > si) { - bcc.back().push_back(st.back()); - st.pop_back(); - }}}} + bcc.emplace_back(si + all(st)); + st.resize(si); + }}} return top; } diff --git a/graph/bellmannFord.cpp b/content/graph/bellmannFord.cpp index 4324886..09ea1aa 100644 --- a/graph/bellmannFord.cpp +++ b/content/graph/bellmannFord.cpp @@ -1,5 +1,5 @@ -void bellmannFord(int n, vector<edge> edges, int start) { - vector<ll> dist(n, INF), parent(n, -1); +auto bellmannFord(int n, vector<edge>& edges, int start) { + vector<ll> dist(n, INF), prev(n, -1); dist[start] = 0; for (int i = 1; i < n; i++) { @@ -7,11 +7,13 @@ void bellmannFord(int n, vector<edge> edges, int start) { if (dist[e.from] != INF && dist[e.from] + e.cost < dist[e.to]) { dist[e.to] = dist[e.from] + e.cost; - parent[e.to] = e.from; + prev[e.to] = e.from; }}} for (edge& e : edges) { if (dist[e.from] != INF && dist[e.from] + e.cost < dist[e.to]) { // Negativer Kreis gefunden. -}}} //return dist, parent?; + }} + return dist; //return prev? +} diff --git a/graph/bitonicTSP.cpp b/content/graph/bitonicTSP.cpp index e8fc2cb..6470232 100644 --- a/graph/bitonicTSP.cpp +++ b/content/graph/bitonicTSP.cpp @@ -1,6 +1,6 @@ vector<vector<double>> dist; // Initialisiere mit Entfernungen zwischen Punkten. -void bitonicTSP() { +auto bitonicTSP() { vector<double> dp(sz(dist), HUGE_VAL); vector<int> pre(sz(dist)); // nur für Tour dp[0] = 0; dp[1] = 2 * dist[0][1]; pre[1] = 0; @@ -27,5 +27,5 @@ void bitonicTSP() { (lt.back() == 1 ? lt : ut).push_back(0); reverse(all(lt)); lt.insert(lt.end(), all(ut)); - //return lt;// Enthält Knoten 0 zweimal. An erster und letzter Position. + return lt;// Enthält Knoten 0 zweimal. An erster und letzter Position. } diff --git a/graph/bitonicTSPsimple.cpp b/content/graph/bitonicTSPsimple.cpp index 96ae5bd..8b6e6c5 100644 --- a/graph/bitonicTSPsimple.cpp +++ b/content/graph/bitonicTSPsimple.cpp @@ -10,7 +10,7 @@ double get(int p1, int p2) { return dp[p1][p2] = min(tryLR, tryRL); } -void bitonicTour() { +auto bitonicTSP() { dp = vector<vector<double>>(sz(dist), vector<double>(sz(dist), -1)); get(0, 0); @@ -23,6 +23,5 @@ void bitonicTour() { rl.push_back(v); p2 = v; }} lr.insert(lr.end(), rl.rbegin(), rl.rend()); - // Enthält Knoten 0 zweimal. An erster und letzter Position. - // return lr; + return lr;// Enthält Knoten 0 zweimal. An erster und letzter Position. } diff --git a/graph/blossom.cpp b/content/graph/blossom.cpp index 7bd494a..7bd494a 100644 --- a/graph/blossom.cpp +++ b/content/graph/blossom.cpp diff --git a/graph/bronKerbosch.cpp b/content/graph/bronKerbosch.cpp index ceeb668..0cfcc5f 100644 --- a/graph/bronKerbosch.cpp +++ b/content/graph/bronKerbosch.cpp @@ -6,14 +6,14 @@ void addEdge(int a, int b) { } void bronKerboschRec(bits R, bits P, bits X) { - if (!P.any() && !X.any()) { + if (P.none() && X.none()) { cliques.push_back(R); } else { int q = min(P._Find_first(), X._Find_first()); bits cands = P & ~adj[q]; - for (int i = 0; i < sz(adj); i++) if (cands[i]){ + for (int i = 0; i < sz(adj); i++) if (cands[i]) { R[i] = 1; - bronKerboschRec(P & adj[i], X & adj[i], R); + bronKerboschRec(R, P & adj[i], X & adj[i]); R[i] = P[i] = 0; X[i] = 1; }}} diff --git a/graph/centroid.cpp b/content/graph/centroid.cpp index 2494464..820945b 100644 --- a/graph/centroid.cpp +++ b/content/graph/centroid.cpp @@ -14,7 +14,7 @@ pair<int, int> dfs_cent(int v, int from, int n) { return {v, -1}; } -pair<int, int> find_centroid(int root) { +pair<int, int> find_centroid(int root = 0) { s.resize(sz(adj)); dfs_sz(root); return dfs_cent(root, -1, s[root]); diff --git a/graph/connect.cpp b/content/graph/connect.cpp index 98b5b25..ffcd6c2 100644 --- a/graph/connect.cpp +++ b/content/graph/connect.cpp @@ -1,7 +1,7 @@ struct connect { int n; vector<pair<int, int>> edges; - LCT lct; // min LCT no updates required + LCT lct; // min LCT @\sourceref{datastructures/LCT.cpp}@, no updates required connect(int n, int m) : n(n), edges(m), lct(n+m) {} diff --git a/graph/cycleCounting.cpp b/content/graph/cycleCounting.cpp index bd7a219..6a299ee 100644 --- a/graph/cycleCounting.cpp +++ b/content/graph/cycleCounting.cpp @@ -1,12 +1,12 @@ constexpr int maxEdges = 128; using cycle = bitset<maxEdges>; -struct cylces { +struct cycles { vector<vector<pair<int, int>>> adj; vector<bool> seen; vector<cycle> paths, base; vector<pair<int, int>> edges; - cylces(int n) : adj(n), seen(n), paths(n) {} + cycles(int n) : adj(n), seen(n), paths(n) {} void addEdge(int u, int v) { adj[u].push_back({v, sz(edges)}); @@ -22,8 +22,8 @@ struct cylces { if (cur.any()) base.push_back(cur); } - void findBase(int v = 0, int from = -1, cycle cur = {}) { - if (adj.empty()) return; + void findBase(int v, int from = -1, cycle cur = {}) { + if (from < 0 && seen[v]) return; if (seen[v]) { addBase(cur ^ paths[v]); } else { @@ -36,8 +36,7 @@ struct cylces { cur[id].flip(); }}} - //cycle must be constrcuted from base - bool isCycle(cycle cur) { + bool isCycle(cycle cur) {//cycle must be constrcuted from base if (cur.none()) return false; init(sz(adj)); // union find @\sourceref{datastructures/unionFind.cpp}@ for (int i = 0; i < sz(edges); i++) { @@ -48,14 +47,15 @@ struct cylces { unionSets(edges[i].first, edges[i].second); }} return cur.none(); - }; + } int count() { - findBase(); + for (int i = 0; i < sz(adj); i++) findBase(i); + assert(sz(base) < 30); int res = 0; for (int i = 1; i < (1 << sz(base)); i++) { cycle cur; - for (int j = 0; j < sz(base); j++) { + for (int j = 0; j < sz(base); j++) if (((i >> j) & 1) != 0) cur ^= base[j]; if (isCycle(cur)) res++; } diff --git a/graph/dfs.tex b/content/graph/dfs.tex index 1e6705f..1e6705f 100644 --- a/graph/dfs.tex +++ b/content/graph/dfs.tex diff --git a/graph/dijkstra.cpp b/content/graph/dijkstra.cpp index 57071b0..61c636d 100644 --- a/graph/dijkstra.cpp +++ b/content/graph/dijkstra.cpp @@ -1,6 +1,6 @@ using path = pair<ll, int>; //dist, destination -void dijkstra(const vector<vector<path>>& adj, int start) { +auto dijkstra(const vector<vector<path>>& adj, int start) { priority_queue<path, vector<path>, greater<path>> pq; vector<ll> dist(sz(adj), INF); vector<int> prev(sz(adj), -1); @@ -17,5 +17,5 @@ void dijkstra(const vector<vector<path>>& adj, int start) { prev[u] = v; pq.emplace(dist[u], u); }}} - //return dist, prev; + return dist; //return prev; } diff --git a/graph/dinicScaling.cpp b/content/graph/dinicScaling.cpp index f4e833a..f4e833a 100644 --- a/graph/dinicScaling.cpp +++ b/content/graph/dinicScaling.cpp diff --git a/graph/euler.cpp b/content/graph/euler.cpp index a5ea192..a5ea192 100644 --- a/graph/euler.cpp +++ b/content/graph/euler.cpp diff --git a/graph/floydWarshall.cpp b/content/graph/floydWarshall.cpp index fb6263e..df096c2 100644 --- a/graph/floydWarshall.cpp +++ b/content/graph/floydWarshall.cpp @@ -1,26 +1,27 @@ vector<vector<ll>> dist; // Entfernung zwischen je zwei Punkten. -vector<vector<int>> pre; +vector<vector<int>> next; void floydWarshall() { - pre.assign(sz(dist), vector<int>(sz(dist), -1)); + next.assign(sz(dist), vector<int>(sz(dist), -1)); for (int i = 0; i < sz(dist); i++) { for (int j = 0; j < sz(dist); j++) { if (dist[i][j] < INF) { - pre[i][j] = j; + next[i][j] = j; }}} for (int k = 0; k < sz(dist); k++) { for (int i = 0; i < sz(dist); i++) { for (int j = 0; j < sz(dist); j++) { + // only needed if dist can be negative + if (dist[i][k] == INF || dist[k][j] == INF) continue; if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; - pre[i][j] = pre[i][k]; + next[i][j] = next[i][k]; }}}}} vector<int> getPath(int u, int v) { - //return dist[u][v]; // Pfadlänge u -> v - if (pre[u][v] < 0) return {}; - vector<int> path = {v}; - while (u != v) path.push_back(u = pre[u][v]); + if (next[u][v] < 0) return {}; + vector<int> path = {u}; + while (u != v) path.push_back(u = next[u][v]); return path; //Pfad u -> v } diff --git a/graph/graph.tex b/content/graph/graph.tex index 9232090..831f4e5 100644 --- a/graph/graph.tex +++ b/content/graph/graph.tex @@ -125,6 +125,7 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da \textbf{Wichtig:} isolierte Knoten und Brücken sind keine BCC. \sourcecode{graph/articulationPoints.cpp} \end{algorithm} +\vfill\null\columnbreak \begin{algorithm}{2-SAT} \sourcecode{graph/2sat.cpp} @@ -169,7 +170,7 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da \sourcecode{graph/virtualTree.cpp} \end{algorithm} -\begin{algorithm}{Maximal Cardinatlity Bipartite Matching} +\begin{algorithm}{Maximum Cardinatlity Bipartite Matching} \label{kuhn} \begin{methods} \method{kuhn}{berechnet Matching}{\abs{V}\*\min(ans^2, \abs{E})} @@ -194,14 +195,6 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da \end{algorithm} \subsection{Max-Flow} -\optional{ -\subsubsection{Capacity Scaling} -\begin{methods} - \method{maxFlow}{gut bei dünn besetzten Graphen.}{\abs{E}^2\*\log(C)} - \method{addEdge}{fügt eine \textbf{gerichtete} Kante ein}{1} -\end{methods} -\sourcecode{graph/capacityScaling.cpp} -} \optional{ \subsubsection{Push Relabel} diff --git a/graph/havelHakimi.cpp b/content/graph/havelHakimi.cpp index cbd6991..ac4d67d 100644 --- a/graph/havelHakimi.cpp +++ b/content/graph/havelHakimi.cpp @@ -3,7 +3,7 @@ vector<vector<int>> havelHakimi(const vector<int>& deg) { for (int i = 0; i < sz(deg); i++) { if (deg[i] > 0) pq.push({deg[i], i}); } - vector<vector<int>> adj; + vector<vector<int>> adj(sz(deg)); while (!pq.empty()) { auto [degV, v] = pq.top(); pq.pop(); if (sz(pq) < degV) return {}; //impossible diff --git a/graph/hld.cpp b/content/graph/hld.cpp index 65d3f5c..65d3f5c 100644 --- a/graph/hld.cpp +++ b/content/graph/hld.cpp diff --git a/graph/hopcroftKarp.cpp b/content/graph/hopcroftKarp.cpp index c1f5d1c..c1f5d1c 100644 --- a/graph/hopcroftKarp.cpp +++ b/content/graph/hopcroftKarp.cpp diff --git a/graph/kruskal.cpp b/content/graph/kruskal.cpp index 987d30b..987d30b 100644 --- a/graph/kruskal.cpp +++ b/content/graph/kruskal.cpp diff --git a/graph/matching.cpp b/content/graph/matching.cpp index 2513604..dcaea8c 100644 --- a/graph/matching.cpp +++ b/content/graph/matching.cpp @@ -15,7 +15,7 @@ int max_matching() { gauss(sz(adj), MOD); //LGS @\sourceref{math/lgsFp.cpp}@ int rank = 0; for (auto& row : mat) { - if (*min_element(all(row)) != 0) rank++; + if (*max_element(all(row)) != 0) rank++; } ans = max(ans, rank / 2); } diff --git a/graph/maxCarBiMatch.cpp b/content/graph/maxCarBiMatch.cpp index e928387..e928387 100644 --- a/graph/maxCarBiMatch.cpp +++ b/content/graph/maxCarBiMatch.cpp diff --git a/graph/maxWeightBipartiteMatching.cpp b/content/graph/maxWeightBipartiteMatching.cpp index a2b0a80..a2b0a80 100644 --- a/graph/maxWeightBipartiteMatching.cpp +++ b/content/graph/maxWeightBipartiteMatching.cpp diff --git a/graph/minCostMaxFlow.cpp b/content/graph/minCostMaxFlow.cpp index 14a222c..14a222c 100644 --- a/graph/minCostMaxFlow.cpp +++ b/content/graph/minCostMaxFlow.cpp diff --git a/graph/pushRelabel.cpp b/content/graph/pushRelabel.cpp index 904aec6..73a9eae 100644 --- a/graph/pushRelabel.cpp +++ b/content/graph/pushRelabel.cpp @@ -9,8 +9,8 @@ vector<ll> ec; vector<int> cur, H; void addEdge(int u, int v, ll c) { - adj[u].push_back({v, (int)sz(adj[v]), 0, c}); - adj[v].push_back({u, (int)sz(adj[u])-1, 0, 0}); + adj[u].push_back({v, (int)sz(adj[v]), 0, c}); + adj[v].push_back({u, (int)sz(adj[u])-1, 0, 0}); } void addFlow(Edge& e, ll f) { diff --git a/graph/reroot.cpp b/content/graph/reroot.cpp index 4c6a748..4c6a748 100644 --- a/graph/reroot.cpp +++ b/content/graph/reroot.cpp diff --git a/graph/scc.cpp b/content/graph/scc.cpp index ac9a40b..ac9a40b 100644 --- a/graph/scc.cpp +++ b/content/graph/scc.cpp diff --git a/graph/stoerWagner.cpp b/content/graph/stoerWagner.cpp index 97e667a..97e667a 100644 --- a/graph/stoerWagner.cpp +++ b/content/graph/stoerWagner.cpp diff --git a/graph/treeIsomorphism.cpp b/content/graph/treeIsomorphism.cpp index 4e9ddce..355fefb 100644 --- a/graph/treeIsomorphism.cpp +++ b/content/graph/treeIsomorphism.cpp @@ -1,5 +1,5 @@ vector<vector<int>> adj; -map<vector<int>, int> known; +map<vector<int>, int> known; // dont reset! int treeLabel(int v, int from = -1) { vector<int> children; diff --git a/graph/virtualTree.cpp b/content/graph/virtualTree.cpp index 2fcea80..27d2d6c 100644 --- a/graph/virtualTree.cpp +++ b/content/graph/virtualTree.cpp @@ -3,8 +3,8 @@ vector<int> in, out; void virtualTree(vector<int> ind) { // indices of used nodes sort(all(ind), [&](int x, int y) {return in[x] < in[y];}); - for (int i=0; i<sz(a)-1; i++) { - ind.push_back(lca(ind[i], ind[i+1])); + for (int i = 0, n = sz(ind); i < n - 1; i++) { + ind.push_back(lca(ind[i], ind[i + 1])); } sort(all(ind), [&](int x, int y) {return in[x] < in[y];}); ind.erase(unique(all(ind)), ind.end()); @@ -12,10 +12,10 @@ void virtualTree(vector<int> ind) { // indices of used nodes int n = ind.size(); vector<vector<int>> tree(n); vector<int> st = {0}; - for (int i=1; i<n; i++) { + for (int i = 1; i < n; i++) { while (in[ind[i]] >= out[ind[st.back()]]) st.pop_back(); tree[st.back()].push_back(i); - st.push(i); + st.push_back(i); } // virtual directed tree with n nodes, original indices in ind // weights can be calculated, e.g. with binary lifting diff --git a/latexHeaders/code.sty b/content/latexHeaders/code.sty index a889596..3ebdda3 100644 --- a/latexHeaders/code.sty +++ b/content/latexHeaders/code.sty @@ -119,7 +119,23 @@ \node[anchor=base west, fill=orange!30,outer sep=0pt,inner xsep=2.2pt, inner ysep=0pt, rounded corners=3pt, minimum height=\ht\strutbox+1pt,#1]{\raisebox{1pt}{\strut}\strut\usebox{#2}}; }% } -\makeatother \newcommand{\hl}[1]{\btHL[fill=safeOrange,draw=black,thin]{#1}} +\ifthenelse{\isundefined{\gitorigin}}{}{ + \usepackage{ocgx2} + \usepackage{fontawesome} + \lst@AddToHook{Init}{% + \ifthenelse{\equal{\lst@name}{}}{}{% + \begin{minipage}[t][0pt]{\linewidth}% + \vspace{0pt}% + \hfill% + \begin{ocg}[printocg=never]{Source links}{srclinks}{1}% + \hfill\href{\gitorigin\lst@name}{\faExternalLink}% + \end{ocg}% + \end{minipage}% + }% + } +} +\makeatother + diff --git a/latexHeaders/commands.sty b/content/latexHeaders/commands.sty index edbba1b..edbba1b 100644 --- a/latexHeaders/commands.sty +++ b/content/latexHeaders/commands.sty diff --git a/latexHeaders/layout.sty b/content/latexHeaders/layout.sty index 096cf23..096cf23 100644 --- a/latexHeaders/layout.sty +++ b/content/latexHeaders/layout.sty diff --git a/latexHeaders/math.sty b/content/latexHeaders/math.sty index c34cc99..c34cc99 100644 --- a/latexHeaders/math.sty +++ b/content/latexHeaders/math.sty diff --git a/math/berlekampMassey.cpp b/content/math/berlekampMassey.cpp index 29e084f..29e084f 100644 --- a/math/berlekampMassey.cpp +++ b/content/math/berlekampMassey.cpp diff --git a/math/bigint.cpp b/content/math/bigint.cpp index 6f83a93..1b3b953 100644 --- a/math/bigint.cpp +++ b/content/math/bigint.cpp @@ -2,6 +2,7 @@ constexpr ll base = 1'000'000; constexpr ll base_digits = 6; struct bigint { + using vll = vector<ll>; vll a; ll sign; bigint() : sign(1) {} @@ -10,11 +11,6 @@ struct bigint { bigint(const string &s) {read(s);} - void operator=(const bigint& v) { - sign = v.sign; - a = v.a; - } - void operator=(ll v) { sign = 1; if (v < 0) sign = -1, v = -v; @@ -255,17 +251,17 @@ struct bigint { } bigint operator*(const bigint& v) const { - vll a(this->a.begin(), this->a.end()); - vll b(v.a.begin(), v.a.end()); - while (sz(a) < sz(b)) a.push_back(0); - while (sz(b) < sz(a)) b.push_back(0); - while (sz(a) & (sz(a) - 1)) - a.push_back(0), b.push_back(0); - vll c = karatsubaMultiply(a, b); + vll ta(a.begin(), a.end()); + vll va(v.a.begin(), v.a.end()); + while (sz(ta) < sz(va)) ta.push_back(0); + while (sz(va) < sz(ta)) va.push_back(0); + while (sz(ta) & (sz(ta) - 1)) + ta.push_back(0), va.push_back(0); + vll ra = karatsubaMultiply(ta, va); bigint res; res.sign = sign * v.sign; - for (ll i = 0, carry = 0; i < sz(c); i++) { - ll cur = c[i] + carry; + for (ll i = 0, carry = 0; i < sz(ra); i++) { + ll cur = ra[i] + carry; res.a.push_back(cur % base); carry = cur / base; } diff --git a/math/binomial0.cpp b/content/math/binomial0.cpp index 896a0f1..5f2ccaa 100644 --- a/math/binomial0.cpp +++ b/content/math/binomial0.cpp @@ -10,5 +10,5 @@ void precalc() { ll calc_binom(ll n, ll k) { if (n < 0 || n < k || k < 0) return 0; - return (inv[n] * inv[n-k] % mod) * fac[k] % mod; + return (inv[k] * inv[n-k] % mod) * fac[n] % mod; } diff --git a/math/binomial1.cpp b/content/math/binomial1.cpp index dab20b3..dab20b3 100644 --- a/math/binomial1.cpp +++ b/content/math/binomial1.cpp diff --git a/math/binomial2.cpp b/content/math/binomial2.cpp index 4531505..4531505 100644 --- a/math/binomial2.cpp +++ b/content/math/binomial2.cpp diff --git a/math/binomial3.cpp b/content/math/binomial3.cpp index f52337c..7a6ab4e 100644 --- a/math/binomial3.cpp +++ b/content/math/binomial3.cpp @@ -1,5 +1,5 @@ ll calc_binom(ll n, ll k, ll p) { - assert(n < p) //wichtig: sonst falsch! + assert(n < p); //wichtig: sonst falsch! if (k > n) return 0; ll x = k % 2 != 0 ? p-1 : 1; for (ll c = p-1; c > n; c--) { diff --git a/math/chineseRemainder.cpp b/content/math/chineseRemainder.cpp index ccbc5dc..ccbc5dc 100644 --- a/math/chineseRemainder.cpp +++ b/content/math/chineseRemainder.cpp diff --git a/math/cycleDetection.cpp b/content/math/cycleDetection.cpp index 621af82..5e68c0c 100644 --- a/math/cycleDetection.cpp +++ b/content/math/cycleDetection.cpp @@ -1,4 +1,4 @@ -void cycleDetection(ll x0, function<ll(ll)> f) { +pair<ll, ll> cycleDetection(ll x0, function<ll(ll)> f) { ll a = x0, b = f(x0), length = 1; for (ll power = 1; a != b; b = f(b), length++) { if (power == length) { @@ -13,4 +13,6 @@ void cycleDetection(ll x0, function<ll(ll)> f) { a = f(a); b = f(b); start++; -}} + } + return {start, length}; +} diff --git a/content/math/discreteLogarithm.cpp b/content/math/discreteLogarithm.cpp new file mode 100644 index 0000000..68866e0 --- /dev/null +++ b/content/math/discreteLogarithm.cpp @@ -0,0 +1,17 @@ +ll dlog(ll a, ll b, ll m) { //a > 0! + ll bound = sqrtl(m) + 1; //memory usage bound < p + vector<pair<ll, ll>> vals(bound); + for (ll i = 0, e = 1; i < bound; i++, e = (e * a) % m) { + vals[i] = {e, i}; + } + vals.emplace_back(m, 0); + sort(all(vals)); + ll fact = powMod(a, m - bound - 1, m); + + for (ll i = 0; i < m; i += bound, b = (b * fact) % m) { + auto it = lower_bound(all(vals), pair<ll, ll>{b, 0}); + if (it->first == b) { + return (i + it->second) % m; + }} + return -1; +} diff --git a/content/math/discreteNthRoot.cpp b/content/math/discreteNthRoot.cpp new file mode 100644 index 0000000..403cb3b --- /dev/null +++ b/content/math/discreteNthRoot.cpp @@ -0,0 +1,5 @@ +ll root(ll a, ll b, ll m) { // a > 0! + ll g = findPrimitive(m); + ll c = dlog(powMod(g, a, m), b, m); + return c < 0 ? -1 : powMod(g, c, m); +} diff --git a/math/divisors.cpp b/content/math/divisors.cpp index 5afd4fb..5afd4fb 100644 --- a/math/divisors.cpp +++ b/content/math/divisors.cpp diff --git a/math/extendedEuclid.cpp b/content/math/extendedEuclid.cpp index ecf4a16..ecf4a16 100644 --- a/math/extendedEuclid.cpp +++ b/content/math/extendedEuclid.cpp diff --git a/math/gauss.cpp b/content/math/gauss.cpp index 3e3b7aa..8129fd2 100644 --- a/math/gauss.cpp +++ b/content/math/gauss.cpp @@ -7,7 +7,7 @@ void takeAll(int n, int line) { for (int i = 0; i < n; i++) { if (i == line) continue; double diff = mat[i][line]; - for (int j = 0; j <= n; j++) { + for (int j = 0; j < sz(mat[i]); j++) { mat[i][j] -= diff * mat[line][j]; }}} diff --git a/math/gcd-lcm.cpp b/content/math/gcd-lcm.cpp index a1c63c8..a1c63c8 100644 --- a/math/gcd-lcm.cpp +++ b/content/math/gcd-lcm.cpp diff --git a/math/goldenSectionSearch.cpp b/content/math/goldenSectionSearch.cpp index 20b15e8..28ee4c3 100644 --- a/math/goldenSectionSearch.cpp +++ b/content/math/goldenSectionSearch.cpp @@ -1,15 +1,15 @@ -ld gss(ld l, ld r, function<ld(ld)> f) { +template<typename F> +ld gss(ld l, ld r, F&& f) { ld inv = (sqrt(5.0l) - 1) / 2; ld x1 = r - inv*(r-l), x2 = l + inv*(r-l); ld f1 = f(x1), f2 = f(x2); for (int i = 0; i < 200; i++) { if (f1 < f2) { //change to > to find maximum - u = x2; x2 = x1; f2 = f1; + r = x2; x2 = x1; f2 = f1; x1 = r - inv*(r-l); f1 = f(x1); } else { l = x1; x1 = x2; f1 = f2; x2 = l + inv*(r-l); f2 = f(x2); - } - } + }} return l; } diff --git a/math/inversions.cpp b/content/math/inversions.cpp index 9e47f9b..9e47f9b 100644 --- a/math/inversions.cpp +++ b/content/math/inversions.cpp diff --git a/math/inversionsMerge.cpp b/content/math/inversionsMerge.cpp index 8235b11..8235b11 100644 --- a/math/inversionsMerge.cpp +++ b/content/math/inversionsMerge.cpp diff --git a/math/kthperm.cpp b/content/math/kthperm.cpp index 899dff1..504f09c 100644 --- a/math/kthperm.cpp +++ b/content/math/kthperm.cpp @@ -1,4 +1,4 @@ -vector<ll> kthperm(ll k, ll n) { +vector<ll> kthperm(ll n, ll k) { Tree<ll> t; vector<ll> res(n); for (ll i = 1; i <= n; k /= i, i++) { diff --git a/math/legendre.cpp b/content/math/legendre.cpp index f08755f..b85ea2a 100644 --- a/math/legendre.cpp +++ b/content/math/legendre.cpp @@ -1,4 +1,4 @@ -ll legendre(ll a, ll p) { +ll legendre(ll a, ll p) { // p prim >= 2 ll s = powMod(a, p / 2, p); return s < 2 ? s : -1ll; } diff --git a/math/lgsFp.cpp b/content/math/lgsFp.cpp index 7081fea..0241742 100644 --- a/math/lgsFp.cpp +++ b/content/math/lgsFp.cpp @@ -7,7 +7,7 @@ void takeAll(int n, int line, ll p) { for (int i = 0; i < n; i++) { if (i == line) continue; ll diff = mat[i][line]; - for (int j = 0; j <= n; j++) { + for (int j = 0; j < sz(mat[i]); j++) { mat[i][j] -= (diff * mat[line][j]) % p; mat[i][j] = (mat[i][j] + p) % p; }}} @@ -23,4 +23,4 @@ void gauss(int n, ll mod) { takeAll(n, i, mod); done[i] = true; }} -// für Eindeutigkeit, Existenz etc. siehe LGS über R +// für Eindeutigkeit, Existenz etc. siehe LGS über R @\sourceref{math/gauss.cpp}@ diff --git a/math/linearCongruence.cpp b/content/math/linearCongruence.cpp index cdb5a37..cdb5a37 100644 --- a/math/linearCongruence.cpp +++ b/content/math/linearCongruence.cpp diff --git a/math/linearRecurence.cpp b/content/math/linearRecurence.cpp index 2501e64..2501e64 100644 --- a/math/linearRecurence.cpp +++ b/content/math/linearRecurence.cpp diff --git a/math/linearSieve.cpp b/content/math/linearSieve.cpp index b029b9a..64440dd 100644 --- a/math/linearSieve.cpp +++ b/content/math/linearSieve.cpp @@ -1,34 +1,34 @@ constexpr ll N = 10'000'000; -ll smallest[N], power[N], sieved[N]; +ll small[N], power[N], sieved[N]; vector<ll> primes; -//wird aufgerufen mit (p^k, p, k) für prime p +//wird aufgerufen mit (p^k, p, k) für prime p und k > 0 ll mu(ll pk, ll p, ll k) {return -(k == 1);} ll phi(ll pk, ll p, ll k) {return pk - pk / p;} ll div(ll pk, ll p, ll k) {return k+1;} -ll divSum(ll pk, ll p, ll k) {return (pk*p+1) / (p - 1);} +ll divSum(ll pk, ll p, ll k) {return (pk*p-1) / (p - 1);} ll square(ll pk, ll p, ll k) {return k % 2 ? pk / p : pk;} -ll squareFree(ll pk, ll p, ll k) {return k % 2 ? pk : 1;} +ll squareFree(ll pk, ll p, ll k) {return p;} void sieve() { // O(N) - smallest[1] = power[1] = sieved[1] = 1; + small[1] = power[1] = sieved[1] = 1; for (ll i = 2; i < N; i++) { - if (smallest[i] == 0) { + if (small[i] == 0) { primes.push_back(i); for (ll pk = i, k = 1; pk < N; pk *= i, k++) { - smallest[pk] = i; + small[pk] = i; power[pk] = pk; sieved[pk] = mu(pk, i, k); // Aufruf ändern! }} - for (ll j = 0; i * primes[j] < N && primes[j] < smallest[i]; j++) { + for (ll j=0; i*primes[j] < N && primes[j] < small[i]; j++) { ll k = i * primes[j]; - smallest[k] = power[k] = primes[j]; + small[k] = power[k] = primes[j]; sieved[k] = sieved[i] * sieved[primes[j]]; } - if (i * smallest[i] < N && power[i] != i) { - ll k = i * smallest[i]; - smallest[k] = smallest[i]; - power[k] = power[i] * smallest[i]; + if (i * small[i] < N && power[i] != i) { + ll k = i * small[i]; + small[k] = small[i]; + power[k] = power[i] * small[i]; sieved[k] = sieved[power[k]] * sieved[k / power[k]]; }}} @@ -43,7 +43,8 @@ ll naive(ll n) { // O(sqrt(n)) pk *= p; k++; } while (n % p == 0); - res *= mu(pk, p, k); // Aufruf ändern! + res *= mu(pk, p, k); // Aufruf ändern! }} + if (n > 1) res *= mu(n, n, 1); return res; } diff --git a/math/longestIncreasingSubsequence.cpp b/content/math/longestIncreasingSubsequence.cpp index fcb63b4..fcb63b4 100644 --- a/math/longestIncreasingSubsequence.cpp +++ b/content/math/longestIncreasingSubsequence.cpp diff --git a/math/math.tex b/content/math/math.tex index c157e1b..f99d0d4 100644 --- a/math/math.tex +++ b/content/math/math.tex @@ -1,12 +1,5 @@ \section{Mathe}
-\begin{algorithm}{Zykel Erkennung}
- \begin{methods}
- \method{cycleDetection}{findet Zyklus von $x_0$ und Länge in $f$}{b+l}
- \end{methods}
- \sourcecode{math/cycleDetection.cpp}
-\end{algorithm}
-
\begin{algorithm}{Longest Increasing Subsequence}
\begin{itemize}
\item \code{lower\_bound} $\Rightarrow$ streng monoton
@@ -14,6 +7,14 @@ \end{itemize}
\sourcecode{math/longestIncreasingSubsequence.cpp}
\end{algorithm}
+\vfill\null\columnbreak
+
+\begin{algorithm}{Zykel Erkennung}
+ \begin{methods}
+ \method{cycleDetection}{findet Zyklus von $x_0$ und Länge in $f$}{b+l}
+ \end{methods}
+ \sourcecode{math/cycleDetection.cpp}
+\end{algorithm}
\begin{algorithm}{Permutationen}
\begin{methods}
@@ -78,7 +79,7 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \begin{algorithm}{Lineare Kongruenz}
\begin{itemize}
- \item Löst $ax\equiv b\pmod{m}$.
+ \item Kleinste Lösung $x$ für $ax\equiv b\pmod{m}$.
\item Weitere Lösungen unterscheiden sich um \raisebox{2pt}{$\frac{m}{g}$}, es gibt
also $g$ Lösungen modulo $m$.
\end{itemize}
@@ -119,12 +120,54 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \sourcecode{math/divisors.cpp}
\end{algorithm}
-\begin{algorithm}{Numerisch Extremstelle bestimmen}
- \sourcecode{math/goldenSectionSearch.cpp}
+\begin{algorithm}{Matrix-Exponent}
+ \begin{methods}
+ \method{precalc}{berechnet $m^{2^b}$ vor}{\log(b)\*n^3}
+ \method{calc}{berechnet $m^b\cdot$}{\log(b)\cdot n^2}
+ \end{methods}
+ \textbf{Tipp:} wenn \code{v[x]=1} und \code{0} sonst, dann ist \code{res[y]} = $m^b_{y,x}$.
+ \sourcecode{math/matrixPower.cpp}
\end{algorithm}
-\begin{algorithm}{Numerisch Integrieren, Simpsonregel}
- \sourcecode{math/simpson.cpp}
+\begin{algorithm}{Lineare Rekurrenz}
+ \begin{methods}
+ \method{BerlekampMassey}{Berechnet eine lineare Rekurrenz $n$-ter Ordnung}{n^2}
+ \method{}{aus den ersten $2n$ Werte}{}
+ \end{methods}
+ \sourcecode{math/berlekampMassey.cpp}
+ Sei $f(n)=c_{0}f(n-1)+c_{1}f(n-2)+\dots + c_{n-1}f(0)$ eine lineare Rekurrenz.
+
+ \begin{methods}
+ \method{kthTerm}{Berechnet $k$-ten Term einer Rekurrenz $n$-ter Ordnung}{\log(k)\cdot n^2}
+ \end{methods}
+ \sourcecode{math/linearRecurence.cpp}
+ Alternativ kann der \mbox{$k$-te} Term in \runtime{n^3\log(k)} berechnet werden:
+ $$\renewcommand\arraystretch{1.5}
+ \setlength\arraycolsep{3pt}
+ \begin{pmatrix}
+ c_{0} & c_{1} & \smash{\cdots} & \smash{\cdots} & c_{n-1} \\
+ 1 & 0 & \smash{\cdots} & \smash{\cdots} & 0 \\
+ 0 & \smash{\ddots} & \smash{\ddots} & & \smash{\vdots} \\
+ \smash{\vdots} & \smash{\ddots} & \smash{\ddots} & \smash{\ddots} & \smash{\vdots} \\
+ 0 & \smash{\cdots} & 0 & 1 & 0 \\
+ \end{pmatrix}^k
+ \times~~
+ \begin{pmatrix}
+ f(n-1) \\
+ f(n-2) \\
+ \smash{\vdots} \\
+ \smash{\vdots} \\
+ f(0) \\
+ \end{pmatrix}
+ ~~=~~
+ \begin{pmatrix}
+ f(n-1+k) \\
+ f(n-2+k) \\
+ \smash{\vdots} \\
+ \smash{\vdots} \\
+ f(k) \makebox[0pt][l]{\hspace{15pt}$\vcenter{\hbox{\huge$\leftarrow$}}$}\\
+ \end{pmatrix}
+ $$
\end{algorithm}
\begin{algorithm}{Diskreter Logarithmus}
@@ -133,15 +176,15 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \end{methods}
\sourcecode{math/discreteLogarithm.cpp}
\end{algorithm}
-%TODO
-\begin{algorithm}{Diskrete \textrm{\textit{n}}-te Wurzel}
+
+\begin{algorithm}{Diskrete Quadratwurzel}
\begin{methods}
- \method{root}{bestimmt Lösung $x$ für $x^a=b \bmod m$ }{\sqrt{m}\*\log(m)}
+ \method{sqrtMod}{bestimmt Lösung $x$ für $x^2=a \bmod p$ }{\log(p)}
\end{methods}
- Alle Lösungen haben die Form $g^{c + \frac{i \cdot \phi(n)}{\gcd(a, \phi(n))}}$
- \sourcecode{math/discreteNthRoot.cpp}
+ \textbf{Wichtig:} $p$ muss prim sein!
+ \sourcecode{math/sqrtModCipolla.cpp}
\end{algorithm}
-
+%\columnbreak
\begin{algorithm}{Primitivwurzeln}
\begin{itemize}
@@ -158,21 +201,57 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \sourcecode{math/primitiveRoot.cpp}
\end{algorithm}
-\begin{algorithm}{Linearessieb und Multiplikative Funktionen}
+\begin{algorithm}{Diskrete \textrm{\textit{n}}-te Wurzel}
+ \begin{methods}
+ \method{root}{bestimmt Lösung $x$ für $x^a=b \bmod m$ }{\sqrt{m}\*\log(m)}
+ \end{methods}
+ Alle Lösungen haben die Form $g^{c + \frac{i \cdot \phi(n)}{\gcd(a, \phi(n))}}$
+ \sourcecode{math/discreteNthRoot.cpp}
+\end{algorithm}
+
+\begin{algorithm}{\textsc{Legendre}-Symbol}
+ Sei $p \geq 3$ eine Primzahl, $a \in \mathbb{Z}$:
+ \vspace{-0.15cm}\begin{align*}
+ \hspace*{3cm}\legendre{a}{p} &=
+ \begin{cases*}
+ \hphantom{-}0 & falls $p~\vert~a$ \\[-1ex]
+ \hphantom{-}1 & falls $\exists x \in \mathbb{Z}\backslash p\mathbb{Z} : a \equiv x^2 \bmod p$ \\[-1ex]
+ -1 & sonst
+ \end{cases*} \\
+ \legendre{-1}{p} = (-1)^{\frac{p - 1}{2}} &=
+ \begin{cases*}
+ \hphantom{-}1 & falls $p \equiv 1 \bmod 4$ \\[-1ex]
+ -1 & falls $p \equiv 3 \bmod 4$
+ \end{cases*} \\
+ \legendre{2}{p} = (-1)^{\frac{p^2 - 1}{8}} &=
+ \begin{cases*}
+ \hphantom{-}1 & falls $p \equiv \pm 1 \bmod 8$ \\[-1ex]
+ -1 & falls $p \equiv \pm 3 \bmod 8$
+ \end{cases*}
+ \end{align*}
+ \begin{align*}
+ \legendre{p}{q} \cdot \legendre{q}{p} = (-1)^{\frac{p - 1}{2} \cdot \frac{q - 1}{2}} &&
+ \legendre{a}{p} \equiv a^{\frac{p-1}{2}}\bmod p
+ \end{align*}
+ \vspace{-0.05cm}
+ \sourcecode{math/legendre.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Lineares Sieb und Multiplikative Funktionen}
Eine (zahlentheoretische) Funktion $f$ heißt multiplikativ wenn $f(1)=1$ und $f(a\cdot b)=f(a)\cdot f(b)$, falls $\ggT(a,b)=1$.
$\Rightarrow$ Es ist ausreichend $f(p^k)$ für alle primen $p$ und alle $k$ zu kennen.
\begin{methods}
\method{sieve}{berechnet Primzahlen und co.}{N}
- \method{sieved}{Wert der endsprechenden Multiplikativen Funktion}{1}
+ \method{sieved}{Wert der entsprechenden multiplikativen Funktion}{1}
- \method{naive}{Wert der endsprechenden Multiplikativen Funktion}{\sqrt{n}}
+ \method{naive}{Wert der entsprechenden multiplikativen Funktion}{\sqrt{n}}
\end{methods}
\textbf{Wichtig:} Sieb rechts ist schneller für \code{isPrime} oder \code{primes}!
\sourcecode{math/linearSieve.cpp}
- \textbf{\textsc{Möbius}-Funtkion:}
+ \textbf{\textsc{Möbius}-Funktion:}
\begin{itemize}
\item $\mu(n)=+1$, falls $n$ quadratfrei ist und gerade viele Primteiler hat
\item $\mu(n)=-1$, falls $n$ quadratfrei ist und ungerade viele Primteiler hat
@@ -219,28 +298,26 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: Für jedes $x$, sei $cnt[x]$ die Anzahl der Vielfachen von $x$ in $A$.
Es gibt $2^{[x]}-1$ nicht leere Subsequences in $A$, die nur Vielfache von $x$ enthalten.
Die Anzahl der Subsequences mit $\ggT=1$ ist gegeben durch $\sum_{i = 1}^N \mu(i) \cdot (2^{cnt[i]} - 1)$.
- %\sourcecode{math/mobius.cpp}
\end{algorithm}
-\optional{
-\columnbreak
-\subsection{\textsc{Euler}sche $\boldsymbol{\varphi}$-Funktion}
-\begin{itemize}
- \item Zählt die relativ primen Zahlen $\leq n$.
-
- \item Multiplikativ:
- $\gcd(a,b) = 1 \Longrightarrow \varphi(a) \cdot \varphi(b) = \varphi(ab)$
-
- \item $p$ prim, $k \in \mathbb{N}$:
- $~\varphi(p^k) = p^k - p^{k - 1}$
-
- \item \textbf{\textsc{Euler}'s Theorem:}
- Für $b \geq \varphi(c)$ gilt: $a^b \equiv a^{b \bmod \varphi(c) + \varphi(c)} \pmod{c}$. Darüber hinaus gilt: $\gcd(a, c) = 1 \Leftrightarrow a^b \equiv a^{b \bmod \varphi(c)} \pmod{c}$.
- Falls $m$ prim ist, liefert das den \textbf{kleinen Satz von \textsc{Fermat}}:
- $a^{m} \equiv a \pmod{m}$
-\end{itemize}
-\sourcecode{math/phi.cpp}
-}
+\subsection{LGS über $\boldsymbol{\mathbb{F}_p}$}
+\method{gauss}{löst LGS}{n^3}
+\sourcecode{math/lgsFp.cpp}
+
+\subsection{LGS über $\boldsymbol{\mathbb{R}}$}
+\method{gauss}{löst LGS}{n^3}
+\sourcecode{math/gauss.cpp}
+
+\vfill\null\columnbreak
+
+\begin{algorithm}{Numerisch Extremstelle bestimmen}
+ \sourcecode{math/goldenSectionSearch.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Numerisch Integrieren, Simpsonregel}
+ \sourcecode{math/simpson.cpp}
+\end{algorithm}
+
\begin{algorithm}{Polynome, FFT, NTT \& andere Transformationen}
Multipliziert Polynome $A$ und $B$.
@@ -252,13 +329,10 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \item Für ganzzahlige Koeffizienten: \code{(ll)round(real(a[i]))}
\item \emph{xor}, \emph{or} und \emph{and} Transform funktioniert auch mit \code{double} oder modulo einer Primzahl $p$ falls $p \geq 2^{\texttt{bits}}$
\end{itemize}
- %\lstinputlisting{math/fft.cpp}
- %\lstinputlisting{math/ntt.cpp}
- %\textcolor{safeOrange}{$\blacksquare$} NTT code, %\textcolor{safeGreen}{$\blacksquare$} FFT code
+ %\sourcecode{math/fft.cpp}
+ %\sourcecode{math/ntt.cpp}
\sourcecode{math/transforms/fft.cpp}
\sourcecode{math/transforms/ntt.cpp}
- \vfill\null
- \columnbreak
\sourcecode{math/transforms/bitwiseTransforms.cpp}
Multiplikation mit 2 transforms statt 3: (nur benutzten wenn nötig!)
\sourcecode{math/transforms/fftMul.cpp}
@@ -268,100 +342,6 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: \sourcecode{math/transforms/seriesOperations.cpp}
\end{algorithm}
-\subsection{LGS über $\boldsymbol{\mathbb{F}_p}$}
-\method{gauss}{löst LGS}{n^3}
-\sourcecode{math/lgsFp.cpp}
-
-\subsection{LGS über $\boldsymbol{\mathbb{R}}$}
-\method{gauss}{löst LGS}{n^3}
-\sourcecode{math/gauss.cpp}
-
-\begin{algorithm}{\textsc{Legendre}-Symbol}
- Sei $p \geq 3$ eine Primzahl, $a \in \mathbb{Z}$:
- \begin{align*}
- \legendre{a}{p} &=
- \begin{cases*}
- \hphantom{-}0 & falls $p~\vert~a$ \\[-1ex]
- \hphantom{-}1 & falls $\exists x \in \mathbb{Z}\backslash p\mathbb{Z} : a \equiv x^2 \bmod p$ \\[-1ex]
- -1 & sonst
- \end{cases*} \\
- \legendre{-1}{p} = (-1)^{\frac{p - 1}{2}} &=
- \begin{cases*}
- \hphantom{-}1 & falls $p \equiv 1 \bmod 4$ \\[-1ex]
- -1 & falls $p \equiv 3 \bmod 4$
- \end{cases*} \\
- \legendre{2}{p} = (-1)^{\frac{p^2 - 1}{8}} &=
- \begin{cases*}
- \hphantom{-}1 & falls $p \equiv \pm 1 \bmod 8$ \\[-1ex]
- -1 & falls $p \equiv \pm 3 \bmod 8$
- \end{cases*}
- \end{align*}
- \begin{align*}
- \legendre{p}{q} \cdot \legendre{q}{p} = (-1)^{\frac{p - 1}{2} \cdot \frac{q - 1}{2}} &&
- \legendre{a}{p} \equiv a^{\frac{p-1}{2}}\bmod p
- \end{align*}
- \sourcecode{math/legendre.cpp}
-\end{algorithm}
-
-\optional{
-\subsection{Primzahlzählfunktion $\boldsymbol{\pi}$}
-\begin{methods}
- \method{init}{berechnet $\pi$ bis $N$}{N\*\log(\log(N))}
- \method{phi}{zählt zu $p_i$ teilerfremde Zahlen $\leq n$ für alle $i \leq k$}{???}
- \method{pi}{zählt Primzahlen $\leq n$ ($n < N^2$)}{n^{2/3}}
-\end{methods}
-\sourcecode{math/piLehmer.cpp}
-}
-
-\begin{algorithm}{Lineare Rekurrenz}
- \begin{methods}
- \method{BerlekampMassey}{Berechnet eine lineare Rekurrenz $n$-ter Ordnung}{n^2}
- \method{}{aus den ersten $2n$ Werte}{}
- \end{methods}
- \sourcecode{math/berlekampMassey.cpp}
- Sei $f(n)=c_{n-1}f(n-1)+c_{n-2}f(n-2)+\dots + c_0f(0)$ eine lineare Rekurrenz.
-
- \begin{methods}
- \method{kthTerm}{Berechnet $k$-ten Term einer Rekurrenz $n$-ter Ordnung}{\log(k)\cdot n^2}
- \end{methods}
- \sourcecode{math/linearRecurence.cpp}
- Alternativ kann der \mbox{$k$-te} Term in \runtime{n^3\log(k)} berechnet werden:
- $$\renewcommand\arraystretch{1.5}
- \setlength\arraycolsep{3pt}
- \begin{pmatrix}
- c_{n-1} & c_{n-2} & \smash{\cdots} & \smash{\cdots} & c_0 \\
- 1 & 0 & \smash{\cdots} & \smash{\cdots} & 0 \\
- 0 & \smash{\ddots} & \smash{\ddots} & & \smash{\vdots} \\
- \smash{\vdots} & \smash{\ddots} & \smash{\ddots} & \smash{\ddots} & \smash{\vdots} \\
- 0 & \smash{\cdots} & 0 & 1 & 0 \\
- \end{pmatrix}^k
- \times~~
- \begin{pmatrix}
- f(n-1) \\
- f(n-2) \\
- \smash{\vdots} \\
- \smash{\vdots} \\
- f(0) \\
- \end{pmatrix}
- ~~=~~
- \begin{pmatrix}
- f(n-1+k) \\
- f(n-2+k) \\
- \smash{\vdots} \\
- \smash{\vdots} \\
- f(k) \makebox[0pt][l]{\hspace{15pt}$\vcenter{\hbox{\huge$\leftarrow$}}$}\\
- \end{pmatrix}
- $$
-\end{algorithm}
-
-\begin{algorithm}{Matrix-Exponent}
- \begin{methods}
- \method{precalc}{berechnet $m^{2^b}$ vor}{\log(b)\*n^3}
- \method{calc}{berechnet $m^b_{y,x}$}{\log(b)\cdot n^2}
- \end{methods}
- \sourcecode{math/matrixPower.cpp}
-\end{algorithm}
-
\begin{algorithm}{Inversionszahl}
\sourcecode{math/inversions.cpp}
\end{algorithm}
@@ -528,6 +508,16 @@ Die Anzahl der Partitionen von $n$ mit Elementen aus ${1,\dots,k}$. \subsection{The Twelvefold Way \textnormal{(verteile $n$ Bälle auf $k$ Boxen)}}
\input{math/tables/twelvefold}
+\optional{
+\subsection{Primzahlzählfunktion $\boldsymbol{\pi}$}
+\begin{methods}
+ \method{init}{berechnet $\pi$ bis $N$}{N\*\log(\log(N))}
+ \method{phi}{zählt zu $p_i$ teilerfremde Zahlen $\leq n$ für alle $i \leq k$}{???}
+ \method{pi}{zählt Primzahlen $\leq n$ ($n < N^2$)}{n^{2/3}}
+\end{methods}
+\sourcecode{math/piLehmer.cpp}
+}
+
%\input{math/tables/numbers}
\begin{algorithm}[optional]{Big Integers}
diff --git a/math/matrixPower.cpp b/content/math/matrixPower.cpp index 05e29f6..d981e6e 100644 --- a/math/matrixPower.cpp +++ b/content/math/matrixPower.cpp @@ -1,16 +1,14 @@ vector<mat> pows; void precalc(mat m) { - pows = {mat(1), m}; + pows = {mat(sz(m.m), 1), m}; for (int i = 1; i < 60; i++) pows.push_back(pows[i] * pows[i]); } -ll calc(int x, int y, ll b) { - vector<ll> v(pows[0].m.size()); - v[x] = 1; +auto calc(ll b, vector<ll> v) { for (ll i = 1; b > 0; i++) { if (b & 1) v = pows[i] * v; b /= 2; } - return v[y]; + return v; } diff --git a/math/millerRabin.cpp b/content/math/millerRabin.cpp index cb27d29..cb27d29 100644 --- a/math/millerRabin.cpp +++ b/content/math/millerRabin.cpp diff --git a/math/modExp.cpp b/content/math/modExp.cpp index 2329a94..2329a94 100644 --- a/math/modExp.cpp +++ b/content/math/modExp.cpp diff --git a/math/modMulIterativ.cpp b/content/math/modMulIterativ.cpp index 611f09a..611f09a 100644 --- a/math/modMulIterativ.cpp +++ b/content/math/modMulIterativ.cpp diff --git a/math/modPowIterativ.cpp b/content/math/modPowIterativ.cpp index 0dc3fb1..0dc3fb1 100644 --- a/math/modPowIterativ.cpp +++ b/content/math/modPowIterativ.cpp diff --git a/math/multInv.cpp b/content/math/multInv.cpp index 647dc2d..647dc2d 100644 --- a/math/multInv.cpp +++ b/content/math/multInv.cpp diff --git a/math/permIndex.cpp b/content/math/permIndex.cpp index 4cffc12..4cffc12 100644 --- a/math/permIndex.cpp +++ b/content/math/permIndex.cpp diff --git a/math/piLegendre.cpp b/content/math/piLegendre.cpp index 21b974b..21b974b 100644 --- a/math/piLegendre.cpp +++ b/content/math/piLegendre.cpp diff --git a/math/piLehmer.cpp b/content/math/piLehmer.cpp index 56c172d..17df85e 100644 --- a/math/piLehmer.cpp +++ b/content/math/piLehmer.cpp @@ -5,7 +5,7 @@ ll memoB[cacheB + 1]; ll memoC[N];
void init() {
- primeSieve(); // code from above
+ primeSieve(); // @\sourceref{math/primeSieve.cpp}@
for (ll i = 0; i < N; i++) {
memoC[i] = memoC[i - 1];
if (isPrime(i)) memoC[i]++;
diff --git a/math/polynomial.cpp b/content/math/polynomial.cpp index 44f6207..44f6207 100644 --- a/math/polynomial.cpp +++ b/content/math/polynomial.cpp diff --git a/math/primeSieve.cpp b/content/math/primeSieve.cpp index 1b0f514..1b0f514 100644 --- a/math/primeSieve.cpp +++ b/content/math/primeSieve.cpp diff --git a/content/math/primitiveRoot.cpp b/content/math/primitiveRoot.cpp new file mode 100644 index 0000000..39a0f64 --- /dev/null +++ b/content/math/primitiveRoot.cpp @@ -0,0 +1,23 @@ +bool isPrimitive(ll g, ll n, ll phi, map<ll, int>& phiFacts) { + if (g == 1) return n == 2; + if (gcd(g, n) > 1) return false; + for (auto [f, _] : phiFacts) + if (powMod(g, phi / f, n) == 1) return false; + return true; +} + +bool isPrimitive(ll g, ll n) { + ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 + map<ll, int> phiFacts; + factor(phin, phiFacts); + return isPrimitive(g, n, phin, phiFacts); +} + +ll findPrimitive(ll n) { //test auf existens geht schneller + ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 + map<ll, int> phiFacts; + factor(phin, phiFacts); + for (ll res = 1; res < n; res++) // oder zufällige Reihenfolge + if (isPrimitive(res, n, phin, phiFacts)) return res; + return -1; +} diff --git a/math/rho.cpp b/content/math/rho.cpp index 7885196..ad640cd 100644 --- a/math/rho.cpp +++ b/content/math/rho.cpp @@ -2,8 +2,8 @@ using lll = __int128; ll rho(ll n) { // Findet Faktor < n, nicht unbedingt prim. if (n % 2 == 0) return 2; ll x = 0, y = 0, prd = 2, i = n/2 + 7; - auto f = [&](lll x){return (x * x + i) % n;}; - for (ll t = 30, i = n/2 + 7; t % 40 || gcd(prd, n) == 1; t++) { + auto f = [&](lll c){return (c * c + i) % n;}; + for (ll t = 30; t % 40 || gcd(prd, n) == 1; t++) { if (x == y) x = ++i, y = f(x); if (ll q = (lll)prd * abs(x-y) % n; q) prd = q; x = f(x); y = f(f(y)); diff --git a/content/math/shortModInv.cpp b/content/math/shortModInv.cpp new file mode 100644 index 0000000..f696cce --- /dev/null +++ b/content/math/shortModInv.cpp @@ -0,0 +1,3 @@ +ll multInv(ll x, ll m) { // x^{-1} mod m + return 1 < x ? m - multInv(m % x, x) * m / x : 1; +} diff --git a/math/simpson.cpp b/content/math/simpson.cpp index a99b911..7f237a4 100644 --- a/math/simpson.cpp +++ b/content/math/simpson.cpp @@ -1,4 +1,4 @@ -double f(double x) {return x;} +//double f(double x) {return x;} double simps(double a, double b) { return (f(a) + 4.0 * f((a + b) / 2.0) + f(b)) * (b - a) / 6.0; diff --git a/content/math/sqrtModCipolla.cpp b/content/math/sqrtModCipolla.cpp new file mode 100644 index 0000000..1fac0c5 --- /dev/null +++ b/content/math/sqrtModCipolla.cpp @@ -0,0 +1,14 @@ +ll sqrtMod(ll a, ll p) {// teste mit legendre ob lösung existiert + if (a < 2) return a; + ll t = 0; + while (legendre((t*t-4*a) % p, p) >= 0) t = rng() % p; + ll b = -t, c = -t, d = 1, m = p; + for (m++; m /= 2; b = (a+a-b*b) % p, a = (a*a) % p) { + if (m % 2) { + d = (c-d*b) % p; + c = (c*a) % p; + } else { + c = (d*a - c*b) % p; + }} + return (d + p) % p; +} diff --git a/math/squfof.cpp b/content/math/squfof.cpp index 1cb97de..1cb97de 100644 --- a/math/squfof.cpp +++ b/content/math/squfof.cpp diff --git a/math/tables.tex b/content/math/tables.tex index 53f3758..53f3758 100644 --- a/math/tables.tex +++ b/content/math/tables.tex diff --git a/math/tables/binom.tex b/content/math/tables/binom.tex index 878a6b0..878a6b0 100644 --- a/math/tables/binom.tex +++ b/content/math/tables/binom.tex diff --git a/math/tables/composite.tex b/content/math/tables/composite.tex index 8e14b2e..c261db1 100644 --- a/math/tables/composite.tex +++ b/content/math/tables/composite.tex @@ -1,27 +1,27 @@ -\begin{tabularx}{\linewidth}{|r||r|r||r|r|r||C|} +\begin{tabularx}{\linewidth}{|r||r||r|r||r|r|r||C|} \hline - \multicolumn{7}{|c|}{Important Numbers} \\ + \multicolumn{8}{|c|}{Important Numbers} \\ \hline - $10^x$ & Highly Composite & \# Divs & $<$ Prime & $>$ Prime & \# Primes & \\ + $10^x$ & Highly Composite & \# Divs & $<$ Prime & $>$ Prime & \# Primes & primorial & \\ \hline - 1 & 6 & 4 & $-3$ & $+1$ & 4 & \\ - 2 & 60 & 12 & $-3$ & $+1$ & 25 & \\ - 3 & 840 & 32 & $-3$ & $+9$ & 168 & \\ - 4 & 7\,560 & 64 & $-27$ & $+7$ & 1\,229 & \\ - 5 & 83\,160 & 128 & $-9$ & $+3$ & 9\,592 & \\ - 6 & 720\,720 & 240 & $-17$ & $+3$ & 78\,498 & \\ - 7 & 8\,648\,640 & 448 & $-9$ & $+19$ & 664\,579 & \\ - 8 & 73\,513\,440 & 768 & $-11$ & $+7$ & 5\,761\,455 & \\ - 9 & 735\,134\,400 & 1\,344 & $-63$ & $+7$ & 50\,847\,534 & \\ - 10 & 6\,983\,776\,800 & 2\,304 & $-33$ & $+19$ & 455\,052\,511 & \\ - 11 & 97\,772\,875\,200 & 4\,032 & $-23$ & $+3$ & 4\,118\,054\,813 & \\ - 12 & 963\,761\,198\,400 & 6\,720 & $-11$ & $+39$ & 37\,607\,912\,018 & \\ - 13 & 9\,316\,358\,251\,200 & 10\,752 & $-29$ & $+37$ & 346\,065\,536\,839 & \\ - 14 & 97\,821\,761\,637\,600 & 17\,280 & $-27$ & $+31$ & 3\,204\,941\,750\,802 & \\ - 15 & 866\,421\,317\,361\,600 & 26\,880 & $-11$ & $+37$ & 29\,844\,570\,422\,669 & \\ - 16 & 8\,086\,598\,962\,041\,600 & 41\,472 & $-63$ & $+61$ & 279\,238\,341\,033\,925 & \\ - 17 & 74\,801\,040\,398\,884\,800 & 64\,512 & $-3$ & $+3$ & 2\,623\,557\,157\,654\,233 & \\ - 18 & 897\,612\,484\,786\,617\,600 & 103\,680 & $-11$ & $+3$ & 24\,739\,954\,287\,740\,860 & \\ + 1 & 6 & 4 & $-3$ & $+1$ & 4 & 2 & \\ + 2 & 60 & 12 & $-3$ & $+1$ & 25 & 3 & \\ + 3 & 840 & 32 & $-3$ & $+9$ & 168 & 4 & \\ + 4 & 7\,560 & 64 & $-27$ & $+7$ & 1\,229 & 5 & \\ + 5 & 83\,160 & 128 & $-9$ & $+3$ & 9\,592 & 6 & \\ + 6 & 720\,720 & 240 & $-17$ & $+3$ & 78\,498 & 7 & \\ + 7 & 8\,648\,640 & 448 & $-9$ & $+19$ & 664\,579 & 8 & \\ + 8 & 73\,513\,440 & 768 & $-11$ & $+7$ & 5\,761\,455 & 8 & \\ + 9 & 735\,134\,400 & 1\,344 & $-63$ & $+7$ & 50\,847\,534 & 9 & \\ + 10 & 6\,983\,776\,800 & 2\,304 & $-33$ & $+19$ & 455\,052\,511 & 10 & \\ + 11 & 97\,772\,875\,200 & 4\,032 & $-23$ & $+3$ & 4\,118\,054\,813 & 10 & \\ + 12 & 963\,761\,198\,400 & 6\,720 & $-11$ & $+39$ & 37\,607\,912\,018 & 11 & \\ + 13 & 9\,316\,358\,251\,200 & 10\,752 & $-29$ & $+37$ & 346\,065\,536\,839 & 12 & \\ + 14 & 97\,821\,761\,637\,600 & 17\,280 & $-27$ & $+31$ & 3\,204\,941\,750\,802 & 12 & \\ + 15 & 866\,421\,317\,361\,600 & 26\,880 & $-11$ & $+37$ & 29\,844\,570\,422\,669 & 13 & \\ + 16 & 8\,086\,598\,962\,041\,600 & 41\,472 & $-63$ & $+61$ & 279\,238\,341\,033\,925 & 13 & \\ + 17 & 74\,801\,040\,398\,884\,800 & 64\,512 & $-3$ & $+3$ & 2\,623\,557\,157\,654\,233 & 14 & \\ + 18 & 897\,612\,484\,786\,617\,600 & 103\,680 & $-11$ & $+3$ & 24\,739\,954\,287\,740\,860 & 16 & \\ \hline \end{tabularx} diff --git a/math/tables/nim.tex b/content/math/tables/nim.tex index 8490d42..8490d42 100644 --- a/math/tables/nim.tex +++ b/content/math/tables/nim.tex diff --git a/math/tables/numbers.tex b/content/math/tables/numbers.tex index 1dc9f38..1dc9f38 100644 --- a/math/tables/numbers.tex +++ b/content/math/tables/numbers.tex diff --git a/math/tables/platonic.tex b/content/math/tables/platonic.tex index f4ee554..f4ee554 100644 --- a/math/tables/platonic.tex +++ b/content/math/tables/platonic.tex diff --git a/math/tables/probability.tex b/content/math/tables/probability.tex index f265d10..f265d10 100644 --- a/math/tables/probability.tex +++ b/content/math/tables/probability.tex diff --git a/math/tables/series.tex b/content/math/tables/series.tex index 3042781..3042781 100644 --- a/math/tables/series.tex +++ b/content/math/tables/series.tex diff --git a/math/tables/stuff.tex b/content/math/tables/stuff.tex index 5b5093e..3cf8b4c 100644 --- a/math/tables/stuff.tex +++ b/content/math/tables/stuff.tex @@ -23,7 +23,7 @@ \#Wälder mit $k$ gewurzelten Bäumen mit vorgegebenen Wurzelknoten& $\frac{k}{n}n^{n-k}$ \\ - Dearangements & + Derangements & $!n = (n - 1)(!(n - 1) + !(n - 2)) = \left\lfloor\frac{n!}{e} + \frac{1}{2}\right\rfloor$ \\ & $\lim\limits_{n \to \infty} \frac{!n}{n!} = \frac{1}{e}$ \\ diff --git a/math/tables/twelvefold.tex b/content/math/tables/twelvefold.tex index 18d3955..18d3955 100644 --- a/math/tables/twelvefold.tex +++ b/content/math/tables/twelvefold.tex diff --git a/math/transforms/andTransform.cpp b/content/math/transforms/andTransform.cpp index 1fd9f5c..1fd9f5c 100644 --- a/math/transforms/andTransform.cpp +++ b/content/math/transforms/andTransform.cpp diff --git a/math/transforms/bitwiseTransforms.cpp b/content/math/transforms/bitwiseTransforms.cpp index 28561da..28561da 100644 --- a/math/transforms/bitwiseTransforms.cpp +++ b/content/math/transforms/bitwiseTransforms.cpp diff --git a/math/transforms/fft.cpp b/content/math/transforms/fft.cpp index 2bd95b2..2bd95b2 100644 --- a/math/transforms/fft.cpp +++ b/content/math/transforms/fft.cpp diff --git a/content/math/transforms/fftMul.cpp b/content/math/transforms/fftMul.cpp new file mode 100644 index 0000000..660ed79 --- /dev/null +++ b/content/math/transforms/fftMul.cpp @@ -0,0 +1,15 @@ +vector<cplx> mul(vector<ll>& a, vector<ll>& b) { + int n = 1 << (__lg(sz(a) + sz(b) - 1) + 1); + vector<cplx> c(all(a)), d(n); + c.resize(n); + for (int i = 0; i < sz(b); i++) c[i] = {real(c[i]), b[i]}; + fft(c); + for (int i = 0; i < n; i++) { + int j = (n - i) & (n - 1); + cplx x = (c[i] + conj(c[j])) / cplx{2, 0}; //fft(a)[i]; + cplx y = (c[i] - conj(c[j])) / cplx{0, 2}; //fft(b)[i]; + d[i] = x * y; + } + fft(d, true); + return d; +} diff --git a/math/transforms/multiplyBitwise.cpp b/content/math/transforms/multiplyBitwise.cpp index 0fa671c..f7cf169 100644 --- a/math/transforms/multiplyBitwise.cpp +++ b/content/math/transforms/multiplyBitwise.cpp @@ -1,5 +1,5 @@ vector<ll> mul(vector<ll> a, vector<ll> b) { - int n = 1 << (__lg(max(sz(a), sz(b)) - 1) + 1); + int n = 1 << (__lg(2 * max(sz(a), sz(b)) - 1)); a.resize(n), b.resize(n); bitwiseConv(a), bitwiseConv(b); for (int i=0; i<n; i++) a[i] *= b[i]; // MOD? diff --git a/math/transforms/multiplyFFT.cpp b/content/math/transforms/multiplyFFT.cpp index 0022d1f..0022d1f 100644 --- a/math/transforms/multiplyFFT.cpp +++ b/content/math/transforms/multiplyFFT.cpp diff --git a/math/transforms/multiplyNTT.cpp b/content/math/transforms/multiplyNTT.cpp index 806d124..806d124 100644 --- a/math/transforms/multiplyNTT.cpp +++ b/content/math/transforms/multiplyNTT.cpp diff --git a/math/transforms/ntt.cpp b/content/math/transforms/ntt.cpp index ca605d3..ca605d3 100644 --- a/math/transforms/ntt.cpp +++ b/content/math/transforms/ntt.cpp diff --git a/math/transforms/orTransform.cpp b/content/math/transforms/orTransform.cpp index eb1da44..eb1da44 100644 --- a/math/transforms/orTransform.cpp +++ b/content/math/transforms/orTransform.cpp diff --git a/math/transforms/seriesOperations.cpp b/content/math/transforms/seriesOperations.cpp index 4743674..4743674 100644 --- a/math/transforms/seriesOperations.cpp +++ b/content/math/transforms/seriesOperations.cpp diff --git a/math/transforms/xorTransform.cpp b/content/math/transforms/xorTransform.cpp index f9d1d82..f9d1d82 100644 --- a/math/transforms/xorTransform.cpp +++ b/content/math/transforms/xorTransform.cpp diff --git a/other/bitOps.cpp b/content/other/bitOps.cpp index 8079305..8079305 100644 --- a/other/bitOps.cpp +++ b/content/other/bitOps.cpp diff --git a/other/compiletime.cpp b/content/other/compiletime.cpp index b71f83b..b71f83b 100644 --- a/other/compiletime.cpp +++ b/content/other/compiletime.cpp diff --git a/other/divideAndConquer.cpp b/content/other/divideAndConquer.cpp index 92ec0ef..830dc7f 100644 --- a/other/divideAndConquer.cpp +++ b/content/other/divideAndConquer.cpp @@ -1,27 +1,27 @@ vector<vector<ll>> dp; vector<vector<ll>> C; -void rec(int i, int j0, int j1, int k0, int k1) { +void rec(int i, int j0, int j1, int m0, int m1) { if (j1 < j0) return; int jmid = (j0 + j1) / 2; dp[i][jmid] = inf; - int bestk = k0; - for (int k = k0; k < min(jmid, k1 + 1); ++k) { + int bestk = m0; + for (int k = m0; k < min(jmid, m1 + 1); ++k) { if (dp[i - 1][k] + C[k + 1][jmid] < dp[i][jmid]) { dp[i][jmid] = dp[i - 1][k] + C[k + 1][jmid]; bestk = k; }} - rec(i, j0, jmid - 1, k0, bestk); - rec(i, jmid + 1, j1, bestk, k1); + rec(i, j0, jmid - 1, m0, bestk); + rec(i, jmid + 1, j1, bestk, m1); } -ll calc(int n, int k) { - dp = vector<vector<ll>>(k, vector<ll>(n, inf)); +ll calc(int n, int m) { + dp = vector<vector<ll>>(m, vector<ll>(n, inf)); for (int i = 0; i < n; i++) dp[0][i] = C[0][i]; - for (int i = 1; i < k; i++) { + for (int i = 1; i < m; i++) { rec(i, 0, n - 1, 0, n - 1); } - return dp[k - 1][n - 1]; + return dp[m - 1][n - 1]; } diff --git a/other/fastIO.cpp b/content/other/fastIO.cpp index 63f9ede..9badcc7 100644 --- a/other/fastIO.cpp +++ b/content/other/fastIO.cpp @@ -1,6 +1,6 @@ void fastscan(int& number) { bool negative = false; - register int c; + int c; number = 0; c = getchar(); while(c != '-' && (c < '0' || c > '9')) c = getchar(); diff --git a/other/josephus2.cpp b/content/other/josephus2.cpp index 5086e13..5086e13 100644 --- a/other/josephus2.cpp +++ b/content/other/josephus2.cpp diff --git a/other/josephusK.cpp b/content/other/josephusK.cpp index 5025f89..5025f89 100644 --- a/other/josephusK.cpp +++ b/content/other/josephusK.cpp diff --git a/other/knuth.cpp b/content/other/knuth.cpp index f619f82..1d513c8 100644 --- a/other/knuth.cpp +++ b/content/other/knuth.cpp @@ -1,9 +1,9 @@ -ll calc(int n, int k, const vector<vector<ll>> &C) { - vector<vector<ll>> dp(k, vector<ll>(n, inf)); - vector<vector<int>> opt(k, vector<int>(n + 1, n - 1)); +ll calc(int n, int m, const vector<vector<ll>>& C) { + vector<vector<ll>> dp(m, vector<ll>(n, inf)); + vector<vector<int>> opt(m, vector<int>(n + 1, n - 1)); for (int i = 0; i < n; i++) dp[0][i] = C[0][i]; - for (int i = 1; i < k; i++) { + for (int i = 1; i < m; i++) { for (int j = n - 1; j >= 0; --j) { opt[i][j] = i == 1 ? 0 : opt[i - 1][j]; for (int k = opt[i][j]; k <= min(opt[i][j+1], j-1); k++) { @@ -11,5 +11,5 @@ ll calc(int n, int k, const vector<vector<ll>> &C) { dp[i][j] = dp[i - 1][k] + C[k + 1][j]; opt[i][j] = k; }}} - return dp[k - 1][n - 1]; + return dp[m - 1][n - 1]; } diff --git a/other/other.tex b/content/other/other.tex index 38434a5..b47893f 100644 --- a/other/other.tex +++ b/content/other/other.tex @@ -49,8 +49,8 @@ \end{algorithm}
\begin{algorithm}{DP Optimizations}
- Aufgabe: Partitioniere Array in genau $k$ zusammenhängende Teile mit minimalen Kosten:
- $dp[i][j] = \min_{k<j}\{dp[i-1][k]+C[k][j]\}$. Es sei $A[i][j]$ das \emph{minimale} optimale
+ Aufgabe: Partitioniere Array in genau $m$ zusammenhängende Teile mit minimalen Kosten:
+ $dp[i][j] = \min_{k<j}\{dp[i-1][k-1]+C[k][j]\}$. Es sei $A[i][j]$ das \emph{minimale} optimale
$k$ bei der Berechnung von $dp[i][j]$.
\paragraph{\textsc{Knuth}-Optimization} Vorbedingung: $A[i - 1][j] \leq A[i][j] \leq A[i][j + 1]$
@@ -61,7 +61,7 @@ \paragraph{Divide and Conquer}
Vorbedingung: $A[i][j - 1] \leq A[i][j]$.
- \method{calc}{berechnet das DP}{k\*n\*\log(n)}
+ \method{calc}{berechnet das DP}{m\*n\*\log(n)}
\sourcecode{other/divideAndConquer.cpp}
\paragraph{Quadrangle inequality} Die Bedingung $\forall a\leq b\leq c\leq d:
@@ -129,7 +129,7 @@ \item \textbf{System von Differenzbeschränkungen:}
Ändere alle Bedingungen in die Form $a-b \leq c$.
- Für jede Bedingung füge eine Kante \texttt{(b,a)} mit Gweicht \texttt{c} ein.
+ Für jede Bedingung füge eine Kante \texttt{(b,a)} mit Gewicht \texttt{c} ein.
Füge Quelle \texttt{s} hinzu, mit Kanten zu allen Knoten mit Gewicht 0.
Nutze \textsc{Bellmann-Ford}, um die kürzesten Pfade von \texttt{s} aus zu finden.
\texttt{d[v]} ist mögliche Lösung für \texttt{v}.
diff --git a/other/pbs.cpp b/content/other/pbs.cpp index 7cb60e5..7cb60e5 100644 --- a/other/pbs.cpp +++ b/content/other/pbs.cpp diff --git a/other/pragmas.cpp b/content/other/pragmas.cpp index a39c850..a39c850 100644 --- a/other/pragmas.cpp +++ b/content/other/pragmas.cpp diff --git a/other/sos.cpp b/content/other/sos.cpp index 01bc44c..01bc44c 100644 --- a/other/sos.cpp +++ b/content/other/sos.cpp diff --git a/content/other/split.cpp b/content/other/split.cpp new file mode 100644 index 0000000..5519f60 --- /dev/null +++ b/content/other/split.cpp @@ -0,0 +1,10 @@ +// Zerlegt s anhand aller Zeichen in delim (verändert s). +vector<string> split(string& s, string delim) { + vector<string> result; char *token; + token = strtok(s.data(), delim.c_str()); + while (token != nullptr) { + result.emplace_back(token); + token = strtok(nullptr, delim.c_str()); + } + return result; +} diff --git a/other/stress.sh b/content/other/stress.sh index d264c2a..d264c2a 100644 --- a/other/stress.sh +++ b/content/other/stress.sh diff --git a/other/stuff.cpp b/content/other/stuff.cpp index 41543ad..41543ad 100644 --- a/other/stuff.cpp +++ b/content/other/stuff.cpp diff --git a/other/timed.cpp b/content/other/timed.cpp index b3ed4ef..b3ed4ef 100644 --- a/other/timed.cpp +++ b/content/other/timed.cpp diff --git a/python/io.py b/content/python/io.py index aa16d4c..aa16d4c 100644 --- a/python/io.py +++ b/content/python/io.py diff --git a/content/python/python.tex b/content/python/python.tex new file mode 100644 index 0000000..a778b85 --- /dev/null +++ b/content/python/python.tex @@ -0,0 +1,10 @@ +\section{Python} +\bgroup +\lstset{language=Python} + +\subsection{Recursion} +\sourcecode{python/recursion.py} + +\subsection{IO} +\sourcecode{python/io.py} +\egroup diff --git a/python/recursion.py b/content/python/recursion.py index 45e0147..45e0147 100644 --- a/python/recursion.py +++ b/content/python/recursion.py diff --git a/string/ahoCorasick.cpp b/content/string/ahoCorasick.cpp index eac312c..eac312c 100644 --- a/string/ahoCorasick.cpp +++ b/content/string/ahoCorasick.cpp diff --git a/string/deBruijn.cpp b/content/string/deBruijn.cpp index e829137..e829137 100644 --- a/string/deBruijn.cpp +++ b/content/string/deBruijn.cpp diff --git a/string/duval.cpp b/content/string/duval.cpp index bf36cce..bf36cce 100644 --- a/string/duval.cpp +++ b/content/string/duval.cpp diff --git a/string/kmp.cpp b/content/string/kmp.cpp index 421479e..421479e 100644 --- a/string/kmp.cpp +++ b/content/string/kmp.cpp diff --git a/string/longestCommonSubsequence.cpp b/content/string/longestCommonSubsequence.cpp index 109fe72..6c9ea44 100644 --- a/string/longestCommonSubsequence.cpp +++ b/content/string/longestCommonSubsequence.cpp @@ -3,7 +3,7 @@ string lcss(const string& a, const string& b) { for (int i = sz(a) - 1; i >= 0; i--) { for (int j = sz(b) - 1; j >= 0; j--) { if (a[i] == b[j]) m[i][j] = 1 + m[i+1][j+1]; - else m[i][j] = maj(m[i+1][j], m[i][j+1]); + else m[i][j] = max(m[i+1][j], m[i][j+1]); }} // Für die Länge: return m[0][0]; string res; for (int j = 0, i = 0; j < sz(b) && i < sz(a);) { diff --git a/string/lyndon.cpp b/content/string/lyndon.cpp index 858c3db..e44379b 100644 --- a/string/lyndon.cpp +++ b/content/string/lyndon.cpp @@ -1,5 +1,5 @@ -bool next(string& s, int n, char mi = '0', char ma = '1') { - for (int i = sz(s), j = sz(s); i < n; i++) +bool next(string& s, int maxLen, char mi = '0', char ma = '1') { + for (int i = sz(s), j = sz(s); i < maxLen; i++) s.push_back(s[i % j]); while(!s.empty() && s.back() == ma) s.pop_back(); if (s.empty()) { diff --git a/string/manacher.cpp b/content/string/manacher.cpp index 112bd55..112bd55 100644 --- a/string/manacher.cpp +++ b/content/string/manacher.cpp diff --git a/string/rollingHash2.cpp b/content/string/rollingHash.cpp index f60db2e..6e914aa 100644 --- a/string/rollingHash2.cpp +++ b/content/string/rollingHash.cpp @@ -5,7 +5,7 @@ struct Hash { vector<ll> pref = {0}, power = {1}; Hash(const string& s) { - for (auto c : s) { + for (auto c : s) { // c > 0 pref.push_back((mul(pref.back(), Q) + c + M) % M); power.push_back(mul(power.back(), Q)); }} diff --git a/content/string/rollingHashCf.cpp b/content/string/rollingHashCf.cpp new file mode 100644 index 0000000..84b2e4e --- /dev/null +++ b/content/string/rollingHashCf.cpp @@ -0,0 +1,17 @@ +// M = 1.7e9 + 9, 1e18L + 9, 2.2e18L + 7 +struct Hash { + static constexpr ll M = 3e18L + 37; + vector<ll> pref = {0}, power = {1}; + + Hash(const string& s, ll Q) { // Q Random in [SIGMA+1, M) + for (auto c : s) { // c > 0 + pref.push_back((mul(pref.back(), Q) + c + M) % M); + power.push_back(mul(power.back(), Q)); + }} + + ll operator()(int l, int r) { + return (pref[r] - mul(power[r-l], pref[l]) + M) % M; + } + + static ll mul(__int128 a, ll b) {return a * b % M;} +}; diff --git a/string/string.tex b/content/string/string.tex index dbea36c..bedabfb 100644 --- a/string/string.tex +++ b/content/string/string.tex @@ -16,7 +16,7 @@ \end{algorithm} \begin{algorithm}{Rolling Hash} - \sourcecode{string/rollingHash2.cpp} + \sourcecode{string/rollingHash.cpp} \end{algorithm} \begin{algorithm}{Pattern Matching mit Wildcards} @@ -105,7 +105,6 @@ \end{algorithm} \begin{algorithm}{Suffix-Automaton} - \sourcecode{string/suffixAutomaton.cpp} \begin{itemize} \item \textbf{Ist \textit{w} Substring von \textit{s}?} Baue Automaten für \textit{s} und wende ihn auf \textit{w} an. @@ -125,6 +124,7 @@ Diese Zahl lässt sich wie oben rekursiv berechnen. Bei jedem Knoten darf nur dann plus 1 gerechnet werden, wenn es ein Terminal ist. \end{itemize} + \sourcecode{string/suffixAutomaton.cpp} \end{algorithm} \begin{algorithm}{Trie} diff --git a/string/suffixArray.cpp b/content/string/suffixArray.cpp index 8b698d2..8b698d2 100644 --- a/string/suffixArray.cpp +++ b/content/string/suffixArray.cpp diff --git a/string/suffixAutomaton.cpp b/content/string/suffixAutomaton.cpp index 291f760..9a68cb3 100644 --- a/string/suffixAutomaton.cpp +++ b/content/string/suffixAutomaton.cpp @@ -3,8 +3,8 @@ constexpr char OFFSET = 'a'; struct SuffixAutomaton { struct State { int len, link = -1; - array<int, ALPHABET_SIZE> next = {}; // map if large Alphabet - State(int l) : len(l) {} + array<int, ALPHABET_SIZE> nxt; // map if large Alphabet + State(int l) : len(l) {fill(all(nxt), -1);} }; vector<State> st = {State(0)}; @@ -19,20 +19,21 @@ struct SuffixAutomaton { int p = cur; cur = sz(st); st.emplace_back(st[p].len + 1); - for (; p != -1 && !st[p].next[c]; p = st[p].link) { - st[p].next[c] = cur; + for (; p != -1 && st[p].nxt[c] < 0; p = st[p].link) { + st[p].nxt[c] = cur; } - if (p == -1) st[cur].link = 0; - else { - int q = st[p].next[c]; + if (p == -1) { + st[cur].link = 0; + } else { + int q = st[p].nxt[c]; if (st[p].len + 1 == st[q].len) { st[cur].link = q; } else { st.emplace_back(st[p].len + 1); st.back().link = st[q].link; - st.back().next = st[q].next; - for (; p != -1 && st[p].next[c] == q; p = st[p].link) { - st[p].next[c] = sz(st) - 1; + st.back().nxt = st[q].nxt; + for (; p != -1 && st[p].nxt[c] == q; p = st[p].link) { + st[p].nxt[c] = sz(st) - 1; } st[q].link = st[cur].link = sz(st) - 1; }}} @@ -47,11 +48,14 @@ struct SuffixAutomaton { // Pair with start index (in t) and length of LCS. pair<int, int> longestCommonSubstring(const string& t) { - int v = 0, l = 0, best = 0, bestp = 0; + int v = 0, l = 0, best = 0, bestp = -1; for (int i = 0; i < sz(t); i++) { int c = t[i] - OFFSET; - for (; v && !st[v].next[c]; v = st[v].link) l = st[v].len; - if (st[v].next[c]) v = st[v].next[c], l++; + while (v > 0 && st[v].nxt[c] < 0) { + v = st[v].link; + l = st[v].len; + } + if (st[v].nxt[c] >= 0) v = st[v].nxt[c], l++; if (l > best) best = l, bestp = i; } return {bestp - best + 1, best}; diff --git a/string/suffixTree.cpp b/content/string/suffixTree.cpp index caeeecf..7112f39 100644 --- a/string/suffixTree.cpp +++ b/content/string/suffixTree.cpp @@ -1,14 +1,14 @@ struct SuffixTree { struct Vert { - int start, end, suf; - map<char, int> next; + int start, end, suf; //s[start...end) along parent edge + map<char, int> nxt; }; string s; int needsSuffix, pos, remainder, curVert, curEdge, curLen; // Each Vertex gives its children range as [start, end) - vector<Vert> tree = {Vert{-1, -1, 0 {}}}; + vector<Vert> tree = {Vert{-1, -1, 0, {}}}; - SuffixTree(const string& s) : s(s) { + SuffixTree(const string& s_) : s(s_) { needsSuffix = remainder = curVert = curEdge = curLen = 0; pos = -1; for (int i = 0; i < sz(s); i++) extend(); @@ -25,7 +25,7 @@ struct SuffixTree { } bool fullImplicitEdge(int vert) { - len = min(tree[vert].end, pos + 1) - tree[vert].start; + int len = min(tree[vert].end, pos + 1) - tree[vert].start; if (curLen >= len) { curEdge += len; curLen -= len; @@ -41,12 +41,12 @@ struct SuffixTree { remainder++; while (remainder) { if (curLen == 0) curEdge = pos; - if (!tree[curVert].next.count(s[curEdge])) { + if (!tree[curVert].nxt.count(s[curEdge])) { int leaf = newVert(pos, sz(s)); - tree[curVert].next[s[curEdge]] = leaf; + tree[curVert].nxt[s[curEdge]] = leaf; addSuffixLink(curVert); } else { - int nxt = tree[curVert].next[s[curEdge]]; + int nxt = tree[curVert].nxt[s[curEdge]]; if (fullImplicitEdge(nxt)) continue; if (s[tree[nxt].start + curLen] == s[pos]) { curLen++; @@ -55,11 +55,11 @@ struct SuffixTree { } int split = newVert(tree[nxt].start, tree[nxt].start + curLen); - tree[curVert].next[s[curEdge]] = split; + tree[curVert].nxt[s[curEdge]] = split; int leaf = newVert(pos, sz(s)); - tree[split].next[s[pos]] = leaf; + tree[split].nxt[s[pos]] = leaf; tree[nxt].start += curLen; - tree[split].next[s[tree[nxt].start]] = nxt; + tree[split].nxt[s[tree[nxt].start]] = nxt; addSuffixLink(split); } remainder--; diff --git a/content/string/trie.cpp b/content/string/trie.cpp new file mode 100644 index 0000000..03cf947 --- /dev/null +++ b/content/string/trie.cpp @@ -0,0 +1,35 @@ +// Zahlenwerte müssen bei 0 beginnen und zusammenhängend sein. +constexpr int ALPHABET_SIZE = 2; +struct node { + int words, ends; + array<int, ALPHABET_SIZE> nxt; + node() : words(0), ends(0) {fill(all(nxt), -1);} +}; +vector<node> trie = {node()}; + +int traverse(const vector<int>& word, int x) { + int id = 0; + for (int c : word) { + if (id < 0 || (trie[id].words == 0 && x <= 0)) return -1; + trie[id].words += x; + if (trie[id].nxt[c] < 0 && x > 0) { + trie[id].nxt[c] = sz(trie); + trie.emplace_back(); + } + id = trie[id].nxt[c]; + } + trie[id].words += x; + trie[id].ends += x; + return id; +} + +int insert(const vector<int>& word) { + return traverse(word, 1); +} + +bool erase(const vector<int>& word) { + int id = traverse(word, 0); + if (id < 0 || trie[id].ends <= 0) return false; + traverse(word, -1); + return true; +} diff --git a/string/z.cpp b/content/string/z.cpp index 069fa38..069fa38 100644 --- a/string/z.cpp +++ b/content/string/z.cpp diff --git a/tcr.tex b/content/tcr.tex index b327b37..b327b37 100644 --- a/tcr.tex +++ b/content/tcr.tex diff --git a/template/console.cpp b/content/template/console.sh index 31885e9..31885e9 100644 --- a/template/console.cpp +++ b/content/template/console.sh diff --git a/template/template.cpp b/content/template/template.cpp index 88bdd56..c9a492c 100644 --- a/template/template.cpp +++ b/content/template/template.cpp @@ -2,8 +2,6 @@ using namespace std; #define tsolve int t; cin >> t; while(t--) solve -#define debug(x) cerr << __LINE__ << ": "#x" = " << (x) << endl -#define nl '\n' #define all(x) ::begin(x), ::end(x) #define sz(x) (ll)::size(x) diff --git a/template/template.tex b/content/template/template.tex index 3525ddf..bf82199 100644 --- a/template/template.tex +++ b/content/template/template.tex @@ -5,5 +5,5 @@ \end{algorithm} \begin{algorithm}{Console} - \sourcecode{template/console.cpp} + \sourcecode{template/console.sh} \end{algorithm} diff --git a/tests/gcc5bug.cpp b/content/tests/gcc5bug.cpp index f49603e..f49603e 100644 --- a/tests/gcc5bug.cpp +++ b/content/tests/gcc5bug.cpp diff --git a/tests/precision.cpp b/content/tests/precision.cpp index 0c81ae1..0c81ae1 100644 --- a/tests/precision.cpp +++ b/content/tests/precision.cpp diff --git a/tests/test.tex b/content/tests/test.tex index 80ac037..80ac037 100644 --- a/tests/test.tex +++ b/content/tests/test.tex diff --git a/tests/whitespace.cpp b/content/tests/whitespace.cpp index d4abf47..d4abf47 100644 --- a/tests/whitespace.cpp +++ b/content/tests/whitespace.cpp diff --git a/datastructures/RMQ.cpp b/datastructures/RMQ.cpp deleted file mode 100644 index 401cca4..0000000 --- a/datastructures/RMQ.cpp +++ /dev/null @@ -1,27 +0,0 @@ -vector<int> values; -vector<vector<int>> rmq; - -int select(int a, int b) { - return values[a] <= values[b] ? a : b; -} - -void build() { - for(int i = 0, s = 1, ss = 1; s <= sz(values); ss=s, s*=2, i++) { - for(int l = 0; l + s <= sz(values); l++) { - if(i == 0) rmq[0][l] = l; - else { - int r = l + ss; - rmq[i][l] = select(rmq[i-1][l], rmq[i-1][r]]); -}}}} - -void init(const vector<int>& v) { - values = v; - rmq = vector<vector<int>>(__lg(sz(values))+1, vector<int>(sz(values))); - build(); -} - -int query(int l, int r) { - if(l >= r) return l; - int s = __lg(r-l); r = r - (1 << s); - return select(rmq[s][l],rmq[s][r]); -} diff --git a/datastructures/fenwickTree.cpp b/datastructures/fenwickTree.cpp deleted file mode 100644 index cac3cf8..0000000 --- a/datastructures/fenwickTree.cpp +++ /dev/null @@ -1,15 +0,0 @@ -vector<ll> tree; - -void update(int i, ll val) { - for (i++; i < sz(tree); i += (i & (-i))) tree[i] += val; -} - -void init(int n) { - tree.assign(n + 1,0); -} - -ll prefix_sum(int i) { - ll sum = 0; - for (i++; i > 0; i -= (i & (-i))) sum += tree[i]; - return sum; -} diff --git a/datastructures/firstUnused.cpp b/datastructures/firstUnused.cpp deleted file mode 100644 index 16b0c21..0000000 --- a/datastructures/firstUnused.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Erste natürliche Zahl nicht im set used. -set<int> used; -int unusedCounter = 1; - -int get_first_unused() { // Laufzeit: O(log n) amortisiert. - auto it = used.lower_bound(unusedCounter); - while (it != used.end() && *it == unusedCounter) { - it++; - unusedCounter++; - } - used.insert(unusedCounter); - return unusedCounter++; -} diff --git a/datastructures/stlPQ.cpp b/datastructures/stlPQ.cpp deleted file mode 100644 index 4e439f8..0000000 --- a/datastructures/stlPQ.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include <ext/pb_ds/priority_queue.hpp> -template<typename T> -// greater<T> für Min-Queue -using priorityQueue = __gnu_pbds::priority_queue<T, less<T>>; - -int main() { - priorityQueue<int> pq; - auto it = pq.push(5); // O(1) - pq.push(7); - pq.pop(); // O(log n) amortisiert - pq.modify(it, 6); // O(log n) amortisiert - pq.erase(it); // O(log n) amortisiert - priorityQueue<int> pq2; - pq.join(pq2); // O(1) -} diff --git a/datastructures/unionFind2.cpp b/datastructures/unionFind2.cpp deleted file mode 100644 index 5362c4d..0000000 --- a/datastructures/unionFind2.cpp +++ /dev/null @@ -1,25 +0,0 @@ -vector<int> uf; - -init(int N) { - uf.assign(N,-1); //-1 indicates that every subset has size 1 -} - -int findSet(int i) { - if(uf[i] < 0) return i; //If uf[i] < 0 we have reach a root - uf[i] = findSet(uf[i]); //Path-Compression - return uf[i]; -} - -void linkSets(int i, int j) { - //Take |uf[i]|, where i must be a root, to get the size - //of the subset - if(abs(uf[i]) < abs(uf[j])) { //Union-by-size. - uf[j] += uf[i]; uf[i] = j; - } else { - uf[i] += uf[j]; uf[j] = i; - } -} - -void unionSets(int i, int j) { - if(findSet(i) != findSet(j)) linkSets(findSet(i),findSet(j)); -} diff --git a/geometry/closestPair.cpp b/geometry/closestPair.cpp deleted file mode 100644 index a2c91b3..0000000 --- a/geometry/closestPair.cpp +++ /dev/null @@ -1,38 +0,0 @@ -bool compY(pt a, pt b) { - return (imag(a) == imag(b)) ? real(a) < real(b) - : imag(a) < imag(b); -} - -bool compX(pt a, pt b) { - return (real(a) == real(b)) ? imag(a) < imag(b) - : real(a) < real(b); -} - -double shortestDist(vector<pt>& pts) { // sz(pts) > 1 - set<pt, bool(*)(pt, pt)> status(compY); - sort(all(pts), compX); - double opt = 1.0/0.0, sqrtOpt = 1.0/0.0; - auto left = pts.begin(), right = pts.begin(); - status.insert(*right); right++; - - while (right != pts.end()) { - if (left != right && - abs(real(*left - *right)) >= sqrtOpt) { - status.erase(*left); - left++; - } else { - auto lower = status.lower_bound({-1.0/0.0, //-INF - imag(*right) - sqrtOpt}); - auto upper = status.upper_bound({-1.0/0.0, //-INF - imag(*right) + sqrtOpt}); - for (;lower != upper; lower++) { - double cand = norm(*right - *lower); - if (cand < opt) { - opt = cand; - sqrtOpt = sqrt(opt); - }} - status.insert(*right); - right++; - }} - return sqrtOpt; -} diff --git a/geometry/linesAndSegments.cpp b/geometry/linesAndSegments.cpp deleted file mode 100644 index 98fe4dc..0000000 --- a/geometry/linesAndSegments.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Test auf Streckenschnitt zwischen a-b und c-d. -bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { - if (orientation(a, b, c) == 0 && orientation(a, b, d) == 0) - return pointOnLineSegment(a,b,c) || - pointOnLineSegment(a,b,d) || - pointOnLineSegment(c,d,a) || - pointOnLineSegment(c,d,b); - return orientation(a, b, c) * orientation(a, b, d) <= 0 && - orientation(c, d, a) * orientation(c, d, b) <= 0; -} - -// Berechnet die Schnittpunkte der Strecken p0-p1 und p2-p3. -// Enthält entweder keinen Punkt, den einzigen Schnittpunkt -// oder die Endpunkte der Schnittstrecke. -vector<pt> lineSegmentIntersection(pt p0, pt p1, pt p2, pt p3) { - double a = cross(p1 - p0, p3 - p2); - double b = cross(p2 - p0, p3 - p2); - double c = cross(p1 - p0, p0 - p2); - if (a < 0) {a = -a; b = -b; c = -c;} - if (b < -EPS || b-a > EPS || c < -EPS || c-a > EPS) return {}; - if (a > EPS) return {p0 + b/a*(p1 - p0)}; - vector<pt> result; - auto insertUnique = [&](pt p) { - for (auto q: result) if (abs(p - q) < EPS) return; - result.push_back(p); - }; - if (dot(p2-p0, p3-p0) < EPS) insertUnique(p0); - if (dot(p2-p1, p3-p1) < EPS) insertUnique(p1); - if (dot(p0-p2, p1-p2) < EPS) insertUnique(p2); - if (dot(p0-p3, p1-p3) < EPS) insertUnique(p3); - return result; -} - -// 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); -} - -// Projiziert p auf die Gerade a-b -pt projectToLine(pt a, pt b, pt p) { - return a + (b - a) * dot(p - a, b - a) / norm(b - a); -} - -// Liegt p auf der Geraden a-b? 2d und 3d -bool pointOnLine(pt a, pt b, pt p) { - return cross(a, b, p) == 0; -} - -// Test auf Linienschnitt zwischen a-b und c-d. -bool lineIntersection(pt a, pt b, pt c, pt d) { - return abs(cross(a - b, c - d)) < EPS; -} - -// Berechnet den Schnittpunkt der Graden p0-p1 und p2-p3. -// die Graden dürfen nicht parallel sein! -pt lineIntersection(pt p0, pt p1, pt p2, pt p3) { - double a = cross(p1 - p0, p3 - p2); - double b = cross(p2 - p0, p3 - p2); - return {p0 + b/a*(p1 - p0)}; -} - -// 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; -} - -// 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 distToLine(a, b, p); -} - -// Kürzeste Entfernung zwischen den Strecken a-b und c-d. -double distBetweenSegments(pt a, pt b, pt c, pt d) { - if (lineSegmentIntersection(a, b, c, d)) return 0.0; - return min({distToSegment(a, b, c), distToSegment(a, b, d), - distToSegment(c, d, a), distToSegment(c, d, b)}); -} - -// sortiert alle Punkte pts auf einer Linie entsprechend dir -void sortLine(pt dir, vector<pt>& pts) { // (2d und 3d) - sort(all(pts), [&](pt a, pt b){ - return dot(dir, a) < dot(dir, b); - }); -} diff --git a/graph/LCA.cpp b/graph/LCA.cpp deleted file mode 100644 index 7debf8f..0000000 --- a/graph/LCA.cpp +++ /dev/null @@ -1,24 +0,0 @@ -vector<vector<int>> adj(); -vector<int> visited(); -vector<int> first(); -vector<int> depth(); - -void initLCA(int gi, int d, int& c) { - visited[c] = gi, depth[c] = d, first[gi] = min(c, first[gi]), c++; - for(int gn : adj[gi]) { - initLCA(gn, d+1, c); - visited[c] = gi, depth[c] = d, c++; -}} - -int getLCA(int a, int b) { - return visited[query(min(first[a], first[b]), max(first[a], first[b]))]; -} - -void exampleUse() { - int c = 0; - visited = vector<int>(2*sz(adj)); - first = vector<int>(sz(adj), 2*sz(adj)); - depth = vector<int>(2*sz(adj)); - initLCA(0, 0, c); - init(depth); -} diff --git a/graph/capacityScaling.cpp b/graph/capacityScaling.cpp deleted file mode 100644 index 90ae654..0000000 --- a/graph/capacityScaling.cpp +++ /dev/null @@ -1,44 +0,0 @@ -struct edge { - int from, to; - ll f, c; -}; - -vector<edge> edges; -vector<vector<int>> adj; -int s, t, dfsCounter; -vector<int> visited; -ll capacity; - -void addEdge(int from, int to, ll c) { - adj[from].push_back(sz(edges)); - edges.push_back({from, to, 0, c}); - adj[to].push_back(sz(edges)); - edges.push_back({to, from, 0, 0}); -} - -bool dfs(int v) { - if (v == t) return true; - if (visited[v] == dfsCounter) return false; - visited[v] = dfsCounter; - for (int id : adj[v]) { - if (edges[id].c >= capacity && dfs(edges[id].to)) { - edges[id].c -= capacity; edges[id ^ 1].c += capacity; - edges[id].f += capacity; edges[id ^ 1].f -= capacity; - return true; - }} - return false; -} - -ll maxFlow(int source, int target) { - capacity = 1ll << 62; - s = source; - t = target; - ll flow = 0; - visited.assign(sz(adj), 0); - dfsCounter = 0; - while (capacity) { - while (dfsCounter++, dfs(s)) flow += capacity; - capacity /= 2; - } - return flow; -} diff --git a/math/discreteLogarithm.cpp b/math/discreteLogarithm.cpp deleted file mode 100644 index d9227b9..0000000 --- a/math/discreteLogarithm.cpp +++ /dev/null @@ -1,14 +0,0 @@ -ll dlog(ll a, ll b, ll m) { - ll bound = sqrtl(m) + 1; //memory usage bound - map<ll, ll> vals; - for (ll i = 0, e = 1; i < bound; i++, e = (e * a) % m) { - vals[e] = i; - } - ll fact = powMod(a, m - bound - 1, m); - - for (ll i = 0; i < m; i += bound, b = (b * fact) % m) { - if (vals.count(b)) { - return i + vals[b]; - }} - return -1; -} diff --git a/math/discreteNthRoot.cpp b/math/discreteNthRoot.cpp deleted file mode 100644 index 7201b2b..0000000 --- a/math/discreteNthRoot.cpp +++ /dev/null @@ -1,5 +0,0 @@ -ll root(ll a, ll b, ll m) { - ll g = findPrimitive(m); - ll c = dlog(powMod(g, a, m), b, m); //dLog @\sourceref{math/discreteLogarithm.cpp}@ - return c < 0 ? -1 : powMod(g, c, m); -} diff --git a/math/mobius.cpp b/math/mobius.cpp deleted file mode 100644 index 3fb4d9e..0000000 --- a/math/mobius.cpp +++ /dev/null @@ -1,21 +0,0 @@ -ll mu(ll n) { // Laufzeit: O(sqrt(n)); - ll res = 1; - for (ll i = 2; i * i <= n; i++) { - if (n % i == 0) { // Optimierung: Nur Primzahlen - if (n % (i * i) == 0) return 0; - res *= -1; - n /= i; - }} - return n > 1 ? -res : res; -} - -// berechnet Möbiusfunktion. Laufzeit: O(N*log(log(N))) -vector<int> mu(n + 1, 1); -for (ll i = 2; i <= n; i++) { - if (mu[i] == 1) { - for (ll j = i; j <= n; j += i) mu[j] *= -2; - for (ll j = i*i; j <= n; j += i*i) mu[j] = 0; - } - // log2(abs(mu[i])) = number of primes - mu[i] = (mu[i] > 0) - (mu[i] < 0); -} diff --git a/math/modSqrt.cpp b/math/modSqrt.cpp deleted file mode 100644 index 367c6c7..0000000 --- a/math/modSqrt.cpp +++ /dev/null @@ -1,23 +0,0 @@ -ll sqrtMod(ll a, ll p) { - assert(powMod(a, (p + 1)/2, p) == 1); //a ist ein quadrat mod p? - if (p % 4 == 3) return powMod(a, (p + 1)/2, p); - if (p % 8 == 5) return powMod(a, (p + 3)/8, p); - ll s = p - 1; - ll r = 0; - while (s % 2 == 0) s /= 2, r++; - ll n = 2; - while (powMod(n, (p - 1)/2, p) != p - 1) n++; - ll x = powMod(a, (s + 1)/2, p); - ll b = powMod(a, s, p); - ll g = powMod(n, s, p); - while (true) { - ll t = b; - ll m = 0; - for (;m < r && t != 1; m++) t = (t * t) % p; - if (t == 1) return x; - ll gs = powMod(g, 1ll << (r - m - 1), p); - g = (gs * gs) % p; - x = (x * gs) % p; - b = (b * g) % p; - r = m; -}} diff --git a/math/phi.cpp b/math/phi.cpp deleted file mode 100644 index 482a139..0000000 --- a/math/phi.cpp +++ /dev/null @@ -1,21 +0,0 @@ -ll phi(ll n) { // Laufzeit: O(sqrt(n)) - // Optimierung: Falls n prim, n - 1 zurückgeben - ll result = n; - for(ll i = 2; i * i <= n; ++i) { - if(n % i == 0) { // Optimierung: Nur Primzahlen - while(n % i == 0) n /= i; - result -= result / i; - }} - if(n > 1) result -= result / n; - return result; -} - -// Sieb, falls alle Werte benötigt werden. -// Laufzeit: O(N*log(log(N))) -vector<ll> phi(n + 1); -for (int i = 1; i <= n; i++) phi[i] = i; -for (int i = 2; i <= n; i++) if (phi[i] == i) { - for (int j = i; j <= n; j += i) { - phi[j] /= i; - phi[j] *= i - 1; -}} diff --git a/math/primitiveRoot.cpp b/math/primitiveRoot.cpp deleted file mode 100644 index 464bdb3..0000000 --- a/math/primitiveRoot.cpp +++ /dev/null @@ -1,23 +0,0 @@ -bool isPrimitive(ll g, ll n, ll phi, map<ll, int> phiFacs) { - if (g == 1) return n == 2; - for (auto [f, _] : phiFacs) - if (powMod(g, phi / f, n) == 1) return false; - return true; -} - -bool isPrimitive(ll g, ll n) { - ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 - map<ll, int> phiFacs; - factor(phin, phiFacs); - return isPrimitive(g, n, phin, phiFacs); -} - -ll findPrimitive(ll n) { - ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 - map<ll, int> phiFacs; - factor(phin, phiFacs); - //auch zufällige Reihenfolge möglich! - for (ll res = 1; res < n; res++) - if (isPrimitive(res, n, phin, phiFacs)) return res; - return -1; -} diff --git a/math/shortModInv.cpp b/math/shortModInv.cpp deleted file mode 100644 index 244bacf..0000000 --- a/math/shortModInv.cpp +++ /dev/null @@ -1,3 +0,0 @@ -ll multInv(ll x, ll m) { // x^{-1} mod m - return 1 < x ? m - inv(m % x, x) * m / x : 1; -} diff --git a/math/sqrtModCipolla.cpp b/math/sqrtModCipolla.cpp deleted file mode 100644 index 12bc590..0000000 --- a/math/sqrtModCipolla.cpp +++ /dev/null @@ -1,13 +0,0 @@ -bool isSquare(ll x, ll p){ - return powMod(x, p/2, p) != p-1; -} - -// Teste vorher, ob sqrt(n) mod p existiert! -ll sqrtMod(ll n, ll p){ - if(n == 0) return 0; - ll r0 = 1, r1 = 0, b0 = 1, b1 = 1, w; - while(isSquare(w=(b0*b0-n+p)%p, p)) b0 = rng()%p; - for(ll e = (p+1)/2; e; e /= 2, tie(b0, b1) = pair((b0*b0 + b1*b1%p*w)%p, 2*b0*b1%p)) - if(e & 1) tie(r0, r1) = pair((r0*b0 + r1*b1%p*w)%p, (r0*b1 + b0*r1)%p); - return r0; -} diff --git a/math/transforms/fftMul.cpp b/math/transforms/fftMul.cpp deleted file mode 100644 index eac343c..0000000 --- a/math/transforms/fftMul.cpp +++ /dev/null @@ -1,14 +0,0 @@ -vector<cplx> mul(vector<cplx>& a, vector<cplx>& b) { - vector<cplx> c(sz(a)), d(sz(a)); - for (int i = 0; i < sz(b); i++) { - c[i] = {real(a[i]), real(b[i])}; - } - fft(c); - for (int i = 0; i < sz(b); i++) { - int j = (sz(a) - i) % sz(a); - cplx x = (c[i] + conj(c[j])) / cplx{2, 0}; //fft(a)[i]; - cplx y = (c[i] - conj(c[j])) / cplx{0, 2}; //fft(b)[i]; - d[i] = x * y; - } - return fft(d, true); -} diff --git a/other/split.cpp b/other/split.cpp deleted file mode 100644 index 5e3966c..0000000 --- a/other/split.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// Zerlegt s anhand aller Zeichen in delim. -vector<string> split(string &s, string delim) { - vector<string> result; char *token; - token = strtok((char*)s.c_str(), (char*)delim.c_str()); - while (token != NULL) { - result.push_back(string(token)); - token = strtok(NULL, (char*)delim.c_str()); - } - return result; -} diff --git a/python/python.tex b/python/python.tex deleted file mode 100644 index e4346bf..0000000 --- a/python/python.tex +++ /dev/null @@ -1,10 +0,0 @@ -\section{Python} -\lstset{language=Python} - -\subsection{IO} -\lstinputlisting{python/io.py} - -\subsection{Recursion} -\lstinputlisting{python/recursion.py} - -\lstset{language=C++} diff --git a/string/rollingHash.cpp b/string/rollingHash.cpp deleted file mode 100644 index 00e2273..0000000 --- a/string/rollingHash.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// q = 29, 53, 101, 257, 1009, 65'537 -// or choose q random from [sigma, m) -// m = 1'500'000'001, 1'600'000'009, 1'700'000'009 -template<ll M, ll Q> -struct Hasher { - vector<ll> power = {1}, pref = {0}; - Hasher(const string& s) { - for (auto x : s) { - power.push_back(power.back() * Q % M); - pref.push_back((pref.back() * Q % M + x) % M); - }} - - ll hash(int l, int r) { // Berechnet hash(s[l..r)). - return (pref[r] - power[r-l] * pref[l] % M + M) % M; - } -}; diff --git a/string/rollingHashCf.cpp b/string/rollingHashCf.cpp deleted file mode 100644 index b055b29..0000000 --- a/string/rollingHashCf.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// q = 29, 53, 101, 257, 1009, 65'537 -// or choose q random from [sigma, m) -// m = 1'500'000'001, 1'600'000'009, 1'700'000'009 -struct Hasher { - vector<ll> power = {1}, pref = {0}; - ll m, q; char c; - Hasher(const string& s, ll m, ll q, char c) : - m(m), q(q), c(c) { - for (char x : s) { - power.push_back(power.back() * q % m); - pref.push_back((pref.back() * q % m + (x - c)) % m); - }} - - ll hash(int l, int r) { // Berechnet hash(s[l..r)). - return (pref[r] - power[r-l] * pref[l] % m + m) % m; - } -}; diff --git a/string/trie.cpp b/string/trie.cpp deleted file mode 100644 index 0544a9f..0000000 --- a/string/trie.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Zahlenwerte müssen bei 0 beginnen und zusammenhängend sein. -constexpr int ALPHABET_SIZE = 2; -struct node { - int words, wordEnds; vector<int> children; - node() : words(0), wordEnds(0), children(ALPHABET_SIZE, -1){} -}; -vector<node> trie = {node()}; - -int insert(vector<int>& word) { - int id = 0; - for (int c : word) { - trie[id].words++; - if (trie[id].children[c] < 0) { - trie[id].children[c] = sz(trie); - trie.emplace_back(); - } - id = trie[id].children[c]; - } - trie[id].words++; - trie[id].wordEnds++; - return id; -} - -void erase(vector<int>& word) { - int id = 0; - for (int c : word) { - trie[id].words--; - id = trie[id].children[c]; - if (id < 0) return; - } - trie[id].words--; - trie[id].wordEnds--; -} Binary files differdiff --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(); +} |
