summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormzuenni <mzuenni@users.noreply.github.com>2024-07-28 22:54:40 +0200
committerGitHub <noreply@github.com>2024-07-28 22:54:40 +0200
commit8d11c6c8213f46f0fa19826917c255edd5d43cb1 (patch)
tree96d75baff33d5a04b5a60f1a41f514a26c716874
parent8c33b4e0d3030cfed17fc64b4fe41133339f6d87 (diff)
Test (#4)
* update * moved content in subdir * rename file * add test setup * add test setup * add github action * automaticly test all cpp files * timeout after 10s * setulimit and dont zero memory * test build pdf * install latexmk * update * update * ngerman * fonts * removed old code * add first test * added tests * test in sorted order * more tests * simplified test * more tests * fix suffix tree * fixes and improvements * done ust lst directly * fix swap * add links to pdf * fix constants * add primorial * add comment * various improvements * more tests * added missing stuf * more tests * fix tests * more tests * more tests * more tests * fix recursion? * test trie * more tests * only use python temporarily for listings * only use python temporarily for listings * more tests * fix longestCommonSubstring * more tests * more tests * made code more similiar * fix? * more tests * more tests * more tests * add ahoCorasick test + limit 4GB stack size * more tests * fix test * add additional test * more tests * more tests * fix? * better fix * fix virtual tree * more tests * more tests * recursive closest pair * more tests * decrease limit * new tests * more tests * fix name * more tests * add test * new test * more tests * more tests * more tests * more tests * new test and content * new code * new code * larger tests * fix and test * new test * new test * update pdf * remove comments * new test * more tests * more testcases * more tests * increased limit * more tests * more tests * more tests * new tests * more tests * shortened code * new test * add basic tests for bigint * more tests * removed old files * new test * ignore some files * more auto more ccw * fix test * more tests * fix * new tests * more tests * more tests * stronger test * actually verify delaunay... * more tests * fix header * more tests * run tests parallel? * test parralel? * add --missing * separate workflows * test * is the pdf checked? * separate workflows * fix workflow * more workflows --------- Co-authored-by: Yidi <noob999noob999@gmail.com>
-rw-r--r--.github/workflows/list_missing.yml9
-rw-r--r--.github/workflows/test_all.yml14
-rw-r--r--.github/workflows/test_datastructures.yml22
-rw-r--r--.github/workflows/test_geometry.yml22
-rw-r--r--.github/workflows/test_graph.yml22
-rw-r--r--.github/workflows/test_math.yml22
-rw-r--r--.github/workflows/test_other.yml22
-rw-r--r--.github/workflows/test_pdf.yml25
-rw-r--r--.github/workflows/test_string.yml22
-rw-r--r--.github/workflows/test_template.yml22
-rw-r--r--.gitignore5
-rw-r--r--Makefile5
-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.cpp15
-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.cpp27
-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.cpp89
-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.cpp17
-rw-r--r--content/math/discreteNthRoot.cpp5
-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.cpp23
-rw-r--r--content/math/rho.cpp (renamed from math/rho.cpp)4
-rw-r--r--content/math/shortModInv.cpp3
-rw-r--r--content/math/simpson.cpp (renamed from math/simpson.cpp)2
-rw-r--r--content/math/sqrtModCipolla.cpp14
-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.cpp15
-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.cpp10
-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.tex10
-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.cpp17
-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.cpp35
-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.cpp27
-rw-r--r--datastructures/fenwickTree.cpp15
-rw-r--r--datastructures/firstUnused.cpp13
-rw-r--r--datastructures/stlPQ.cpp15
-rw-r--r--datastructures/unionFind2.cpp25
-rw-r--r--geometry/closestPair.cpp38
-rw-r--r--geometry/linesAndSegments.cpp89
-rw-r--r--graph/LCA.cpp24
-rw-r--r--graph/capacityScaling.cpp44
-rw-r--r--math/discreteLogarithm.cpp14
-rw-r--r--math/discreteNthRoot.cpp5
-rw-r--r--math/mobius.cpp21
-rw-r--r--math/modSqrt.cpp23
-rw-r--r--math/phi.cpp21
-rw-r--r--math/primitiveRoot.cpp23
-rw-r--r--math/shortModInv.cpp3
-rw-r--r--math/sqrtModCipolla.cpp13
-rw-r--r--math/transforms/fftMul.cpp14
-rw-r--r--other/split.cpp10
-rw-r--r--python/python.tex10
-rw-r--r--string/rollingHash.cpp16
-rw-r--r--string/rollingHashCf.cpp17
-rw-r--r--string/trie.cpp33
-rw-r--r--tcr.pdfbin667098 -> 690769 bytes
-rw-r--r--test/datastructures/bitset.cpp6
-rw-r--r--test/datastructures/fenwickTree.cpp58
-rw-r--r--test/datastructures/fenwickTree2.cpp60
-rw-r--r--test/datastructures/lazyPropagation.cpp61
-rw-r--r--test/datastructures/pbds.cpp11
-rw-r--r--test/datastructures/segmentTree.cpp122
-rw-r--r--test/datastructures/sparseTable.cpp51
-rw-r--r--test/datastructures/sparseTableDisjoint.cpp48
-rw-r--r--test/datastructures/stlHashMap.cpp4
-rw-r--r--test/datastructures/stlTree.cpp2
-rw-r--r--test/datastructures/unionFind.cpp109
-rw-r--r--test/datastructures/waveletTree.cpp75
-rw-r--r--test/geometry.h140
-rw-r--r--test/geometry/antipodalPoints.cpp70
-rw-r--r--test/geometry/circle.cpp116
-rw-r--r--test/geometry/closestPair.cpp69
-rw-r--r--test/geometry/closestPair.double.cpp66
-rw-r--r--test/geometry/convexHull.cpp79
-rw-r--r--test/geometry/delaunay.cpp144
-rw-r--r--test/geometry/formulas.cpp127
-rw-r--r--test/geometry/linesAndSegments.cpp240
-rw-r--r--test/geometry/polygon.cpp296
-rw-r--r--test/geometry/segmentIntersection.cpp88
-rw-r--r--test/geometry/sortAround.cpp83
-rw-r--r--test/geometry/triangle.cpp146
-rw-r--r--test/graph/2sat.cpp133
-rw-r--r--test/graph/LCA_sparse.cpp63
-rw-r--r--test/graph/TSP.cpp67
-rw-r--r--test/graph/articulationPoints.bcc.cpp78
-rw-r--r--test/graph/articulationPoints.bridges.cpp64
-rw-r--r--test/graph/articulationPoints.cpp85
-rw-r--r--test/graph/bellmannFord.cpp70
-rw-r--r--test/graph/bitonicTSP.cpp49
-rw-r--r--test/graph/bitonicTSPsimple.cpp49
-rw-r--r--test/graph/blossom.cpp76
-rw-r--r--test/graph/bronKerbosch.cpp73
-rw-r--r--test/graph/centroid.cpp77
-rw-r--r--test/graph/cycleCounting.cpp79
-rw-r--r--test/graph/dijkstra.cpp64
-rw-r--r--test/graph/dinicScaling.cpp61
-rw-r--r--test/graph/euler.cpp87
-rw-r--r--test/graph/floydWarshall.cpp90
-rw-r--r--test/graph/havelHakimi.cpp65
-rw-r--r--test/graph/hopcroftKarp.cpp74
-rw-r--r--test/graph/kruskal.cpp91
-rw-r--r--test/graph/matching.cpp62
-rw-r--r--test/graph/maxCarBiMatch.cpp74
-rw-r--r--test/graph/maxWeightBipartiteMatching.cpp59
-rw-r--r--test/graph/minCostMaxFlow.cpp68
-rw-r--r--test/graph/pushRelabel.cpp61
-rw-r--r--test/graph/scc.cpp92
-rw-r--r--test/graph/stoerWagner.cpp81
-rw-r--r--test/graph/treeIsomorphism.cpp126
-rw-r--r--test/math/berlekampMassey.cpp68
-rw-r--r--test/math/bigint.cpp122
-rw-r--r--test/math/binomial0.cpp31
-rw-r--r--test/math/binomial1.cpp27
-rw-r--r--test/math/binomial2.cpp29
-rw-r--r--test/math/binomial3.cpp31
-rw-r--r--test/math/chineseRemainder.cpp47
-rw-r--r--test/math/cycleDetection.cpp47
-rw-r--r--test/math/discreteLogarithm.cpp64
-rw-r--r--test/math/discreteNthRoot.cpp78
-rw-r--r--test/math/divisors.cpp65
-rw-r--r--test/math/extendedEuclid.cpp41
-rw-r--r--test/math/gauss.cpp118
-rw-r--r--test/math/gcd-lcm.cpp46
-rw-r--r--test/math/goldenSectionSearch.cpp74
-rw-r--r--test/math/inversions.cpp43
-rw-r--r--test/math/inversionsMerge.cpp46
-rw-r--r--test/math/kthperm.cpp38
-rw-r--r--test/math/kthperm_permIndex.cpp21
-rw-r--r--test/math/legendre.cpp43
-rw-r--r--test/math/lgsFp.cpp118
-rw-r--r--test/math/linearCongruence.cpp53
-rw-r--r--test/math/linearRecurence.cpp54
-rw-r--r--test/math/linearSieve.cpp71
-rw-r--r--test/math/longestIncreasingSubsequence.cpp76
-rw-r--r--test/math/matrixPower.cpp116
-rw-r--r--test/math/millerRabin.base32.cpp137
-rw-r--r--test/math/millerRabin.cpp129
-rw-r--r--test/math/modExp.cpp42
-rw-r--r--test/math/modMulIterativ.cpp57
-rw-r--r--test/math/modPowIterativ.cpp42
-rw-r--r--test/math/multInv.cpp40
-rw-r--r--test/math/permIndex.cpp39
-rw-r--r--test/math/piLegendre.cpp40
-rw-r--r--test/math/piLehmer.cpp42
-rw-r--r--test/math/primeSieve.cpp47
-rw-r--r--test/math/primitiveRoot.cpp82
-rw-r--r--test/math/rho.cpp117
-rw-r--r--test/math/shortModInv.cpp39
-rw-r--r--test/math/simpson.cpp63
-rw-r--r--test/math/sqrtModCipolla.cpp48
-rw-r--r--test/math/transforms/andTransform.cpp38
-rw-r--r--test/math/transforms/bitwiseTransforms.cpp38
-rw-r--r--test/math/transforms/fft.cpp51
-rw-r--r--test/math/transforms/fftMul.cpp62
-rw-r--r--test/math/transforms/multiplyBitwise.cpp55
-rw-r--r--test/math/transforms/multiplyFFT.cpp55
-rw-r--r--test/math/transforms/multiplyNTT.cpp56
-rw-r--r--test/math/transforms/ntt.cpp39
-rw-r--r--test/math/transforms/orTransform.cpp38
-rw-r--r--test/math/transforms/xorTransform.cpp38
-rw-r--r--test/other/compiletime.cpp2
-rw-r--r--test/other/divideAndConquer.cpp103
-rw-r--r--test/other/fastIO.cpp32
-rw-r--r--test/other/fastIO.in2
-rw-r--r--test/other/josephus2.cpp42
-rw-r--r--test/other/josephusK.cpp43
-rw-r--r--test/other/knuth.cpp103
-rw-r--r--test/other/sos.cpp50
-rw-r--r--test/other/split.cpp24
-rw-r--r--test/string/ahoCorasick.cpp76
-rw-r--r--test/string/deBruijn.cpp43
-rw-r--r--test/string/duval.cpp85
-rw-r--r--test/string/kmp.cpp85
-rw-r--r--test/string/longestCommonSubsequence.cpp55
-rw-r--r--test/string/lyndon.cpp61
-rw-r--r--test/string/manacher.cpp49
-rw-r--r--test/string/rollingHash.cpp92
-rw-r--r--test/string/rollingHashCf.cpp94
-rw-r--r--test/string/suffixArray.cpp61
-rw-r--r--test/string/suffixAutomaton.cpp62
-rw-r--r--test/string/suffixTree.cpp50
-rw-r--r--test/string/trie.cpp58
-rw-r--r--test/string/z.cpp41
-rw-r--r--test/template/template.cpp1
-rwxr-xr-xtest/test.sh70
-rw-r--r--test/util.h411
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
diff --git a/.gitignore b/.gitignore
index 21eab22..632f1b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -220,3 +220,8 @@ TSWLatexianTemp*
*-tags.tex
*~
+
+# ignore build dir
+build/*
+# dont ignore build tcr
+!tcr.pdf
diff --git a/Makefile b/Makefile
index 0338a34..b3538cf 100644
--- a/Makefile
+++ b/Makefile
@@ -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--;
-}
diff --git a/tcr.pdf b/tcr.pdf
index 5cc63a7..6cfe7d1 100644
--- a/tcr.pdf
+++ b/tcr.pdf
Binary files differ
diff --git a/test/datastructures/bitset.cpp b/test/datastructures/bitset.cpp
new file mode 100644
index 0000000..2ba61a5
--- /dev/null
+++ b/test/datastructures/bitset.cpp
@@ -0,0 +1,6 @@
+#include "../util.h"
+
+int main() {
+ int x = 0;
+ #include <datastructures/bitset.cpp>
+}
diff --git a/test/datastructures/fenwickTree.cpp b/test/datastructures/fenwickTree.cpp
new file mode 100644
index 0000000..c1ef6bf
--- /dev/null
+++ b/test/datastructures/fenwickTree.cpp
@@ -0,0 +1,58 @@
+#include "../util.h"
+#include <datastructures/fenwickTree.cpp>
+
+//void update(int i, ll val)
+//void init(int n)
+//prefix_sum(int i)
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ init(n);
+ vector<ll> naive(n);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ int i = Random::integer<int>(0, n);
+ ll x = Random::integer<ll>(-1000, 1000);
+ update(i, x);
+ naive[i] += x;
+ }
+ {
+ queries++;
+ int i = Random::integer<int>(0, n);
+ ll got = prefix_sum(i);
+ ll expected = 0;
+ for (int j = 0; j <= i; j++) expected += naive[j];
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ t.start();
+ init(N);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ int i = Random::integer<int>(0, N);
+ int j = Random::integer<int>(0, N);
+ ll x = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ update(i, x);
+ hash ^= prefix_sum(j);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/fenwickTree2.cpp b/test/datastructures/fenwickTree2.cpp
new file mode 100644
index 0000000..aa99576
--- /dev/null
+++ b/test/datastructures/fenwickTree2.cpp
@@ -0,0 +1,60 @@
+#include "../util.h"
+#include <datastructures/fenwickTree2.cpp>
+
+//void update(int l, int r, ll val)
+//void init(int n)
+//prefix_sum(int i)
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ vector<ll> naive(n);// = Random::integers<ll>(n, -1000, 1000);
+ init(naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ auto [i, j] = Random::pair<int>(0, n);
+ ll x = Random::integer<ll>(-1000, 1000);
+ update(i, j, x);
+ for (int k = i; k < j; k++) naive[k] += x;
+ }
+ {
+ queries++;
+ int i = Random::integer<int>(0, n);
+ ll got = prefix_sum(i);
+ ll expected = 0;
+ for (int j = 0; j <= i; j++) expected += naive[j];
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ vector<ll> tmp = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ init(tmp);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ int i = Random::integer<int>(0, N);
+ int j = Random::integer<int>(0, N);
+ int k = Random::integer<int>(0, N);
+ ll x = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ update(i, j, x);
+ hash ^= prefix_sum(k);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/lazyPropagation.cpp b/test/datastructures/lazyPropagation.cpp
new file mode 100644
index 0000000..7002061
--- /dev/null
+++ b/test/datastructures/lazyPropagation.cpp
@@ -0,0 +1,61 @@
+#include "../util.h"
+constexpr ll inf = LL::INF;
+#include <datastructures/lazyPropagation.cpp>
+
+constexpr int N = 1000'000;
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ vector<ll> naive = Random::integers<ll>(n, -1000, 1000);
+ SegTree tree(naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ ll x = Random::integer<ll>(-1000, 1000);
+ tree.update(l, r, x);
+ for (int j = l; j < r; j++) naive[j] = x;
+ }
+ {
+ queries++;
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ ll got = tree.query(l, r);
+ ll expected = 0;
+ for (int j = l; j < r; j++) expected += naive[j];
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test() {
+ timer t;
+ t.start();
+ vector<ll> tmp(N);
+ SegTree tree(tmp);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ auto [l1, r1] = Random::pair<int>(0, N + 1);
+ auto [l2, r2] = Random::pair<int>(0, N + 1);
+ ll x1 = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ tree.update(l1, r1, x1);
+ hash ^= tree.query(l2, r2);
+ t.stop();
+ }
+ if (t.time > 2000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/pbds.cpp b/test/datastructures/pbds.cpp
new file mode 100644
index 0000000..9080332
--- /dev/null
+++ b/test/datastructures/pbds.cpp
@@ -0,0 +1,11 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+
+int main() {
+ Tree<int> t1, t2;
+ swap(t1, t2);
+ hashSet<int> s1, s2;
+ swap(s1, s2);
+ hashMap<int, int> m1, m2;
+ swap(m1, m2);
+} \ No newline at end of file
diff --git a/test/datastructures/segmentTree.cpp b/test/datastructures/segmentTree.cpp
new file mode 100644
index 0000000..fbac13e
--- /dev/null
+++ b/test/datastructures/segmentTree.cpp
@@ -0,0 +1,122 @@
+#include "../util.h"
+#include <datastructures/segmentTree.cpp>
+
+constexpr int N = 1'000'000;
+
+//void update(int i, ll val)
+//ll query(int l, int r)
+
+//point update + range query
+void stress_test1() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ vector<ll> naive = Random::integers<ll>(n, -1000, 1000);
+ SegTree tree(naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ int i = Random::integer<int>(0, n);
+ ll x = Random::integer<ll>(-1000, 1000);
+ tree.update(i, x);
+ naive[i] = x;//point assignment
+ }
+ {
+ queries++;
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ ll got = tree.query(l, r);
+ ll expected = 0;
+ for (int j = l; j < r; j++) expected += naive[j];//range sum
+ if (got != expected) cerr << " got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ cerr << " tested random queries: " << queries << endl;
+}
+
+//point update + range query
+void performance_test1() {
+ timer t;
+ t.start();
+ vector<ll> tmp(N);
+ SegTree tree(tmp);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ int i = Random::integer<int>(0, N);
+ auto [l, r] = Random::pair<int>(0, N + 1);
+ ll x = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ tree.update(i, x);
+ hash ^= tree.query(l, r);
+ t.stop();
+ }
+ if (t.time > 1000) cerr << " too slow: " << t.time << FAIL;
+ cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+//void modify(int l, int r, T val)
+//ll query(int i)
+
+//range update + point query
+void stress_test2() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ vector<ll> naive(n);
+ SegTree tree(naive);
+ naive = Random::integers<ll>(n, -1000, 1000);
+ copy(all(naive), tree.tree.begin() + n);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ ll x = Random::integer<ll>(-1000, 1000);
+ tree.modify(l, r, x);
+ for (int j = l; j < r; j++) naive[j] += x;//range add
+ }
+ {
+ queries++;
+ int i = Random::integer<int>(0, n);
+ ll got = tree.query(i);
+ ll expected = naive[i];//point query
+ if (got != expected) cerr << " got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ cerr << " tested random queries: " << queries << endl;
+}
+
+//range update + point query
+void performance_test2() {
+ timer t;
+ t.start();
+ vector<ll> tmp(N);
+ SegTree tree(tmp);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ int i = Random::integer<int>(0, N);
+ auto [l, r] = Random::pair<int>(0, N + 1);
+ ll x = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ tree.modify(l, r, x);
+ hash ^= tree.query(i);
+ t.stop();
+ }
+ if (t.time > 1000) cerr << " too slow: " << t.time << FAIL;
+ cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ cerr << "point update + range query:" << endl;
+ stress_test1();
+ performance_test1();
+ cerr << "range update + point query" << endl;
+ stress_test2();
+ performance_test2();
+}
diff --git a/test/datastructures/sparseTable.cpp b/test/datastructures/sparseTable.cpp
new file mode 100644
index 0000000..7577694
--- /dev/null
+++ b/test/datastructures/sparseTable.cpp
@@ -0,0 +1,51 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+#include <datastructures/sparseTable.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 1000; tries++) {
+ int n = Random::integer<int>(1, 100);
+ vector<ll> naive = Random::integers<ll>(n, -1000, 1000);
+ SparseTable st;
+ st.init(&naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ queries++;
+ int l = Random::integer<int>(0, n+1);
+ int r = Random::integer<int>(0, n+1);
+
+ ll got = st.queryIdempotent(l, r);
+ ll expected = r <= l ? -1 : l;
+ for (int j = l; j < r; j++) {
+ if (naive[j] < naive[expected]) expected = j;
+ }
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'500'000;
+void performance_test() {
+ timer t;
+ vector<ll> naive = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ SparseTable st;
+ st.init(&naive);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ auto [l, r] = Random::pair<int>(0, N+1);
+
+ t.start();
+ hash += st.queryIdempotent(l, r);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/sparseTableDisjoint.cpp b/test/datastructures/sparseTableDisjoint.cpp
new file mode 100644
index 0000000..77bb005
--- /dev/null
+++ b/test/datastructures/sparseTableDisjoint.cpp
@@ -0,0 +1,48 @@
+#include "../util.h"
+#include <datastructures/sparseTableDisjoint.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 1000; tries++) {
+ int n = Random::integer<int>(1, 100);
+ vector<ll> naive = Random::integers<ll>(n, -1000, 1000);
+ DisjointST st;
+ st.init(&naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ queries++;
+ int l = Random::integer<int>(0, n+1);
+ int r = Random::integer<int>(0, n+1);
+
+ ll got = st.query(l, r);
+ ll expected = 0;
+ for (int j = l; j < r; j++) expected += naive[j];
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'250'000;
+void performance_test() {
+ timer t;
+ vector<ll> naive = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ DisjointST st;
+ st.init(&naive);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ auto [l, r] = Random::pair<int>(0, N+1);
+
+ t.start();
+ hash += st.query(l, r);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/stlHashMap.cpp b/test/datastructures/stlHashMap.cpp
new file mode 100644
index 0000000..77976fd
--- /dev/null
+++ b/test/datastructures/stlHashMap.cpp
@@ -0,0 +1,4 @@
+#include "../util.h"
+#include <datastructures/stlHashMap.cpp>
+
+int main() {} \ No newline at end of file
diff --git a/test/datastructures/stlTree.cpp b/test/datastructures/stlTree.cpp
new file mode 100644
index 0000000..7bacbee
--- /dev/null
+++ b/test/datastructures/stlTree.cpp
@@ -0,0 +1,2 @@
+#include "../util.h"
+#include <datastructures/stlTree.cpp>
diff --git a/test/datastructures/unionFind.cpp b/test/datastructures/unionFind.cpp
new file mode 100644
index 0000000..2afdc86
--- /dev/null
+++ b/test/datastructures/unionFind.cpp
@@ -0,0 +1,109 @@
+#include "../util.h"
+struct UF {
+ UF(int n) {init(n);}
+ #include <datastructures/unionFind.cpp>
+};
+
+struct Naive {
+ vector<vector<int>> adj;
+ vector<int> seen;
+ int counter;
+ Naive(int n) : adj(n), seen(n), counter(0) {}
+
+ template<typename F>
+ void dfs(int x, F&& f) {
+ counter++;
+ vector<int> todo = {x};
+ seen[x] = counter;
+ while (!todo.empty()) {
+ x = todo.back();
+ todo.pop_back();
+ f(x);
+ for (ll y : adj[x]) {
+ if (seen[y] != counter) {
+ seen[y] = counter;
+ todo.push_back(y);
+ }
+ }
+ }
+ }
+
+ int findSet(int a) {
+ int res = a;
+ dfs(a, [&](int x){res = min(res, x);});
+ return res;
+ }
+
+ void unionSets(int a, int b) {
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ }
+
+ int size(int a) {
+ int res = 0;
+ dfs(a, [&](int /**/){res++;});
+ return res;
+ }
+};
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200; tries++) {
+ int n = Random::integer<int>(1, 100);
+ UF uf(n);
+ Naive naive(n);
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 10; j++) {
+ int a = Random::integer<int>(0, n);
+ int b = Random::integer<int>(0, n);
+ uf.unionSets(a, b);
+ naive.unionSets(a, b);
+ }
+ UF tmp = uf;
+ for (int j = 0; j < n; j++) {
+ {
+ auto got = tmp.size(j);
+ auto expected = naive.size(j);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ {
+ int a = Random::integer<int>(0, n);
+ int b = Random::integer<int>(0, n);
+ bool got = tmp.findSet(a) == tmp.findSet(b);
+ bool expected = naive.findSet(a) == naive.findSet(b);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ queries += n;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 2'000'000;
+void performance_test() {
+ timer t;
+ t.start();
+ UF uf(N);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ int i = Random::integer<int>(0, N);
+ int j = Random::integer<int>(0, N);
+ int k = Random::integer<int>(0, N);
+ int l = Random::integer<int>(0, N);
+
+ t.start();
+ uf.unionSets(i, j);
+ hash += uf.size(k);
+ hash += uf.size(l);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/datastructures/waveletTree.cpp b/test/datastructures/waveletTree.cpp
new file mode 100644
index 0000000..d294835
--- /dev/null
+++ b/test/datastructures/waveletTree.cpp
@@ -0,0 +1,75 @@
+#include "../util.h"
+#include <datastructures/waveletTree.cpp>
+
+constexpr int N = 1000'000;
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100; tries++) {
+ int n = Random::integer<int>(10, 100);
+ vector<ll> naive = Random::integers<ll>(n, -1000, 1000);
+ WaveletTree tree(naive);
+ for (int operations = 0; operations < 1000; operations++) {
+ {
+ queries++;
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ int x = Random::integer<int>(-1, n);
+ ll got = tree.kth(l, r, x);
+ ll expected = -1;
+ if (x >= 0 && l + x < r) {
+ vector<ll> tmp(naive.begin() + l, naive.begin() + r);
+ std::sort(all(tmp));
+ expected = tmp[x];
+ }
+ if (got != expected) {
+ cerr << "kth, got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ {
+ queries++;
+ int l = Random::integer<int>(0, n + 1);
+ int r = Random::integer<int>(0, n + 1);
+ //if (l > r) swap(l, r);
+ ll x = Random::integer<ll>(-1000, 1000);
+ ll got = tree.countSmaller(l, r, x);
+ ll expected = 0;
+ for (int j = l; j < r; j++) {
+ if (naive[j] < x) expected++;
+ }
+ if (got != expected) {
+ cerr << "count, got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test() {
+ timer t;
+ vector<ll> tmp = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ WaveletTree tree(tmp);
+ t.stop();
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ auto [l1, r1] = Random::pair<int>(0, N + 1);
+ auto [l2, r2] = Random::pair<int>(0, N + 1);
+ int x1 = Random::integer<ll>(l1, r1 + 1);
+ ll x2 = Random::integer<ll>(-1000, 1000);
+
+ t.start();
+ hash ^= tree.kth(l1, r1, x1);
+ hash ^= tree.countSmaller(l2, r2, x2);
+ t.stop();
+ }
+ if (t.time > 2000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/geometry.h b/test/geometry.h
new file mode 100644
index 0000000..7886fe2
--- /dev/null
+++ b/test/geometry.h
@@ -0,0 +1,140 @@
+#include <geometry/sortAround.cpp>
+
+namespace details {
+ // Liegt p auf der Strecke a-b?
+ bool pointInLineSegment(pt a, pt b, pt p) {
+ if (cross(a, b, p) != 0) return false;
+ auto dist = norm(a - b);
+ return norm(a - p) < dist && norm(b - p) < dist;
+ }
+
+ // Test auf Streckenschnitt zwischen a-b und c-d.
+ // (nur intern)
+ bool lineSegmentIntersection(pt a, pt b, pt c, pt d) {
+ if (cross(a, b, c) == 0 && cross(a, b, d) == 0) {
+ return pointInLineSegment(a,b,c) ||
+ pointInLineSegment(a,b,d) ||
+ pointInLineSegment(c,d,a) ||
+ pointInLineSegment(c,d,b);
+ }
+ return ccw(a, b, c) * ccw(a, b, d) < 0 &&
+ ccw(c, d, a) * ccw(c, d, b) < 0;
+ }
+}
+
+namespace Random {
+ vector<ll> partition(ll n, std::size_t k){//min = 0;
+ n += k;
+ vector<ll> res = Random::distinct<ll>(k-1, 1, n);
+ sort(all(res));
+ res.emplace_back(n);
+ ll last = 0;
+ for (std::size_t i = 0; i < k; i++) {
+ res[i] -= last;
+ last += res[i];
+ res[i]--;
+ }
+ return res;
+ }
+
+ vector<pt> convex(int n, ll dim) {
+ binomial_distribution<int> binomial(n - 2, 0.5);
+
+ while (true) {
+ int left = 1 + binomial(Random::rng);
+ int down = 1 + binomial(Random::rng);
+ auto x = Random::partition(2 * dim - 2, left);
+ auto y = Random::partition(2 * dim - 2, down);
+ for (auto& z : x) z = -z;
+ for (auto& z : y) z = -z;
+ for (auto z : Random::partition(2 * dim - 2, n - left)) x.push_back(z);
+ for (auto z : Random::partition(2 * dim - 2, n - down)) y.push_back(z);
+ auto itX = std::partition(x.begin(), x.end(), [](ll z){return z == 0;});
+ auto itY = std::partition(y.begin(), y.end(), [](ll z){return z != 0;});
+ if (distance(x.begin(), itX) + distance(itY, y.end()) > n) continue;
+ shuffle(itX, x.end(), Random::rng);
+ if (itX != x.begin()) shuffle(y.begin(), itY, Random::rng);
+
+ vector<pt> dirs(n);
+ for (size_t i = 0; i < dirs.size(); i++) {
+ dirs[i] = pt(x[i], y[i]);
+ }
+ sortAround(0, dirs);
+
+ vector<pt> res = {{0, 0}};
+ ll maxX = 0;
+ ll maxY = 0;
+ for (auto dir : dirs) {
+ pt tmp(real(res.back()) + real(dir),
+ imag(res.back()) + imag(dir));
+ maxX = std::max<ll>(maxX, real(tmp));
+ maxY = std::max<ll>(maxY, imag(tmp));
+ res.emplace_back(tmp);
+ }
+ res.pop_back();
+ for (auto& point : res) {
+ point = pt(real(point) + dim - 1 - maxX,
+ imag(point) + dim - 1 - maxY);
+ }
+ bool strict = true;
+ for (int i = 0; i < n; i++) strict &= cross(res[i], res[(i + 1) % n], res[(i + 2) % n]) != 0;
+ if (strict) return res;
+ }
+ }
+
+ vector<pt> polygon(int n, ll dim) {
+ while (true) {
+ vector<pt> ps = points<pt::value_type>(n, -dim, dim);
+ bool coolinear = false;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ for (int k = 0; k < j; k++) {
+ coolinear |= cross(ps[i], ps[j], ps[k]) == 0;
+ }
+ }
+ }
+ if (coolinear) continue;
+
+ bool changed;
+ do {
+ changed = false;
+ for (int i = 0; i < n && !changed; i++) {
+ for (int j = i + 1; j < n && !changed; j++) {
+ if (details::lineSegmentIntersection(ps[i], ps[(i+1) % n], ps[j], ps[(j+1) % n])) {
+ reverse(ps.begin() + i + 1, ps.begin() + j + 1);
+ changed = true;
+ }
+ }
+ }
+ } while (changed);
+ return ps;
+ }
+ }
+
+ pt integerPoint(ll range) {
+ return pt(integer<ll>(-range, range),
+ integer<ll>(-range, range));
+ }
+
+ vector<pt> integerPoints(std::size_t n, ll range) {
+ vector<pt> res(n);
+ for (auto& p : res) p = integerPoint(range);
+ return res;
+ }
+
+ array<pt, 2> line(ll range) {
+ pt a = integerPoint(range);
+ pt b = a;
+ while (b == a) b = integerPoint(range);
+ return {a, b};
+ }
+
+ array<pt, 3> triangle(ll range) {
+ pt a = integerPoint(range);
+ pt b = a;
+ while (b == a) b = integerPoint(range);
+ pt c = a;
+ while (ccw(a, b, c) == 0) c = integerPoint(range);
+ return {a, b, c};
+ }
+} \ No newline at end of file
diff --git a/test/geometry/antipodalPoints.cpp b/test/geometry/antipodalPoints.cpp
new file mode 100644
index 0000000..d20dfb6
--- /dev/null
+++ b/test/geometry/antipodalPoints.cpp
@@ -0,0 +1,70 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+#include <geometry/antipodalPoints.cpp>
+#include "../geometry.h"
+
+vector<pair<int, int>> naive(vector<pt> ps) {
+ ll n = sz(ps);
+ auto test = [&](int i, int j){
+ if (dot(ps[j] - ps[i], ps[i - 1] - ps[i]) <= 0) return false;
+ if (dot(ps[j] - ps[i], ps[i + 1] - ps[i]) <= 0) return false;
+ return true;
+ };
+ ps.push_back(ps[0]);
+ ps.push_back(ps[1]);
+ vector<pair<int, int>> res;
+ for (ll i = 1; i <= n; i++) {
+ for (ll j = 1; j < i; j++) {
+ if (test(i, j) && test(j, i)) res.emplace_back(i % n, j % n);
+ }
+ }
+ return res;
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(3, 30);
+ auto ps = Random::convex(n, range);
+
+ auto got = antipodalPoints(ps);
+ for (auto& [a, b] : got) if (a > b) swap(a, b);
+ sort(all(got));
+
+ auto expected = naive(ps);
+ for (auto& [a, b] : expected) if (a > b) swap(a, b);
+
+ for (auto x : expected) {
+ auto it = lower_bound(all(got), x);
+ if (it == got.end() || *it != x) cerr << "error" << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 99'000;
+void performance_test() {
+ timer t;
+
+ auto ps = Random::convex(N, 1'000'000'000);
+
+ t.start();
+ auto got = antipodalPoints(ps);
+ t.stop();
+
+ hash_t hash = sz(got);
+ if (t.time > 50) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/circle.cpp b/test/geometry/circle.cpp
new file mode 100644
index 0000000..3d3d27d
--- /dev/null
+++ b/test/geometry/circle.cpp
@@ -0,0 +1,116 @@
+#include "../util.h"
+constexpr double EPS = 1e-6;
+#define ll double
+double gcd(double x, double /**/) {return x;} //hacky
+#include <geometry/formulas.cpp>
+#undef ll
+#include <geometry/circle.cpp>
+
+// Entfernung von Punkt p zur Geraden durch a-b. 2d und 3d
+double distToLine(pt a, pt b, pt p) {
+ return abs(cross(p - a, b - a)) / abs(b - a);
+}
+
+pt randomIntegerPT(ll range) {
+ return pt(Random::integer<ll>(-range, range), Random::integer<ll>(-range, range));
+}
+
+ll sq(ll x) {
+ return x*x;
+}
+
+int expectedCount(ll x1, ll y1, ll r1, ll x2, ll y2, ll r2) {
+ if (x1 == x2 && y1 == y2){
+ return r1 == r2 ? -1 : 0;
+ } else {
+ ll d = sq(x1 - x2) + sq(y1 - y2);
+
+ if (d > sq(r1 + r2) || d < sq(r1 - r2)) {
+ return 0;
+ } else if (d == sq(r1 + r2) || d == sq(r1 - r2)) {
+ return 1;
+ } else{
+ return 2;
+ }
+ }
+}
+
+void test_circleIntersection(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto c1 = randomIntegerPT(range);
+ auto c2 = c1;
+ while (c1 == c2) c2 = randomIntegerPT(range);
+ double r1 = Random::integer<ll>(1, range);
+ double r2 = Random::integer<ll>(1, range);
+
+ auto got = circleIntersection(c1, r1, c2, r2);
+
+ if (sz(got) != expectedCount(real(c1), imag(c1), r1, real(c2), imag(c2), r2)) cerr << "error: wrong count" << FAIL;
+
+ for (int i = 0; i < sz(got); i++) {
+ for (int j = 0; j < i; j++) {
+ if (abs(got[i] - got[j]) < 1e-6) cerr << "error: identical" << FAIL;
+ }
+ }
+
+ for (auto p : got) {
+ if (float_error(abs(c1 - p), r1) > 1e-6) cerr << "error: 1" << FAIL;
+ if (float_error(abs(c2 - p), r2) > 1e-6) cerr << "error: 2" << FAIL;
+ }
+ queries += sz(got);
+ }
+ cerr << "tested circleIntersection: " << queries << endl;
+}
+
+void test_circleRayIntersection(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto c = randomIntegerPT(range);
+ double r = Random::integer<ll>(1, range);
+
+ pt orig = randomIntegerPT(range);
+ pt dir = 0;
+ while (abs(dir) < 0.5) dir = randomIntegerPT(range);
+
+ auto got = circleRayIntersection(c, r, orig, dir);
+
+ double dist = distToLine(orig, orig + dir, c);
+ int lineIntersections = 0;
+ if (dist <= r) lineIntersections = 2;
+ if (abs(dist - r) < 1e-9) lineIntersections = 1;
+
+ int expected = 0;
+ if (abs(orig - c) < r) expected = 1; //starts inside
+ if (abs(orig - c) > r) { //starts outside
+ if (dot(dir, c - orig) >= 0) expected = lineIntersections;
+ else expected = 0;
+ }
+ if (abs(abs(orig - c) - r) < 1e-9) { //starts on circle
+ if (dot(dir, c - orig) >= 0) expected = lineIntersections;
+ else expected = 1;
+ }
+
+ if (sz(got) != expected) cerr << "error: wrong count" << FAIL;
+
+ for (int i = 0; i < sz(got); i++) {
+ for (int j = 0; j < i; j++) {
+ if (abs(got[i] - got[j]) < 1e-6) cerr << "error: identical" << FAIL;
+ }
+ }
+
+ for (auto p : got) {
+ if (float_error(abs(c - p), r) > 1e-6) cerr << "error: 1" << FAIL;
+ if (distToLine(orig, orig + dir, p) > 1e-6) cerr << "error: 2" << FAIL;
+ }
+ queries += sz(got);
+ }
+ cerr << "tested circleIntersection: " << queries << endl;
+}
+
+int main() {
+ test_circleIntersection(10);
+ test_circleIntersection(100);
+ test_circleRayIntersection(10);
+ test_circleRayIntersection(100);
+}
diff --git a/test/geometry/closestPair.cpp b/test/geometry/closestPair.cpp
new file mode 100644
index 0000000..5959b21
--- /dev/null
+++ b/test/geometry/closestPair.cpp
@@ -0,0 +1,69 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+constexpr ll INF = LL::INF;
+ll sq(ll x) {return x*x;}
+ll isqrt(ll x) {return (ll)sqrtl(x);}
+#include <geometry/closestPair.cpp>
+
+//strict convex hull
+ll naive(const vector<pt>& ps) {
+ ll opt = LL::INF;
+ for (ll i = 0; i < sz(ps); i++) {
+ for (ll j = 0; j < i; j++) {
+ opt = min(opt, norm(ps[i] - ps[j]));
+ }
+ }
+ return opt;
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 100);
+ auto ps = Random::points<ll>(n, -range, range);
+ auto got = shortestDist(ps);
+ auto expected = naive(ps);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ double maxTime = 0;
+
+ vector<pt> ps;
+ for (int i = 0; i*i <= N; i++) {
+ for (int j = 0; j*j <= N; j++) {
+ ps.emplace_back(i, j);
+ }
+ }
+ t.start();
+ hash = shortestDist(ps);
+ t.stop();
+ maxTime = max(maxTime, t.time);
+
+ ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000);
+ t.reset();
+ t.start();
+ hash += shortestDist(ps);
+ t.stop();
+ maxTime = max(maxTime, t.time);
+
+ if (maxTime > 500) cerr << "too slow: " << maxTime << FAIL;
+ cerr << "tested performance: " << maxTime << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/closestPair.double.cpp b/test/geometry/closestPair.double.cpp
new file mode 100644
index 0000000..2f8a1ab
--- /dev/null
+++ b/test/geometry/closestPair.double.cpp
@@ -0,0 +1,66 @@
+#include "../util.h"
+constexpr double EPS = 1e-9;
+#define ll double
+double gcd(double x, double /**/) {return x;} //hacky
+#include <geometry/formulas.cpp>
+constexpr ll INF = LL::INF;
+#include <geometry/closestPair.cpp>
+#undef ll
+
+//strict convex hull
+double naive(const vector<pt>& ps) {
+ double opt = LL::INF;
+ for (ll i = 0; i < sz(ps); i++) {
+ for (ll j = 0; j < i; j++) {
+ opt = min(opt, norm(ps[i] - ps[j]));
+ }
+ }
+ return opt;
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 100);
+ auto ps = Random::points<double>(n, -range, range);
+ auto got = shortestDist(ps);
+ auto expected = naive(ps);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ double maxTime = 0;
+
+ vector<pt> ps;
+ for (int i = 0; i*i <= N; i++) {
+ for (int j = 0; j*j <= N; j++) {
+ ps.emplace_back(i, j);
+ }
+ }
+ t.start();
+ hash = shortestDist(ps);
+ t.stop();
+ maxTime = max(maxTime, t.time);
+
+ ps = Random::points<double>(N, -1'000'000'000, 1'000'000'000);
+ t.reset();
+ t.start();
+ hash += shortestDist(ps);
+ t.stop();
+ maxTime = max(maxTime, t.time);
+
+ if (maxTime > 500) cerr << "too slow: " << maxTime << FAIL;
+ cerr << "tested performance: " << maxTime << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/convexHull.cpp b/test/geometry/convexHull.cpp
new file mode 100644
index 0000000..788a634
--- /dev/null
+++ b/test/geometry/convexHull.cpp
@@ -0,0 +1,79 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+#include <geometry/convexHull.cpp>
+
+//strict convex hull
+ll isConvexHull(const vector<pt>& ps, const vector<pt>& hull) {
+ ll n = sz(hull) - 1;
+ if (n == 0) {
+ for (pt p : ps) if (p != hull[0]) return 1;
+ return 0;
+ } else {
+ if (hull[0] != hull[n]) return 2;
+ //hull has no duplicates
+ for (ll i = 0; i < n; i++) {
+ for (ll j = 0; j < i; j++) {
+ if (hull[i] == hull[j]) return 3;
+ }
+ }
+ //hull is subset
+ for (pt p : hull) {
+ bool isP = false;
+ for (pt c : ps) isP |= c == p;
+ if (!isP) return 4;
+ }
+ //hull contains all points
+ for (pt p : hull) {
+ ll mi = 1;
+ for (ll i = 0; i < n; i++) {
+ mi = min(mi, cross(hull[i], hull[i + 1], p));
+ }
+ if (mi < 0) return 5; //outside
+ if (mi > 0) continue;
+ bool isCorner = 4;
+ for (pt c : hull) isCorner |= c == p;
+ if (!isCorner) return 6;
+ }
+ // hull is convex
+ if (n <= 2) return 0;
+ for (ll i = 0; i < n; i++) {
+ if (cross(hull[i], hull[i + 1], hull[(i + 2) % n]) <= 0) return 7;
+ }
+ return 0;
+ }
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 100);
+ auto ps = Random::points<ll>(n, -range, range);
+ auto got = convexHull(ps);
+ if (isConvexHull(ps, got) > 0) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 2'000'000;
+void performance_test() {
+ timer t;
+ auto ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000);
+ t.start();
+ auto a = convexHull(ps);
+ t.stop();
+ hash_t hash = sz(a);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/delaunay.cpp b/test/geometry/delaunay.cpp
new file mode 100644
index 0000000..7f8ec30
--- /dev/null
+++ b/test/geometry/delaunay.cpp
@@ -0,0 +1,144 @@
+#include "../util.h"
+using pt = complex<lll>;
+// Kreuzprodukt, 0, falls kollinear.
+auto cross(pt a, pt b) {return imag(conj(a) * b);}
+auto cross(pt p, pt a, pt b) {return cross(a - p, b - p);}
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#include <geometry/delaunay.cpp>
+
+vector<pt> convexHull(vector<pt> pts){
+ sort(all(pts), [](const pt& a, const pt& b){
+ return real(a) == real(b) ? imag(a) < imag(b)
+ : real(a) < real(b);
+ });
+ pts.erase(unique(all(pts)), pts.end());
+ int k = 0;
+ vector<pt> h(2 * sz(pts));
+ auto half = [&](auto begin, auto end, int t) {
+ for (auto it = begin; it != end; it++) {
+ while (k > t && cross(h[k-2], h[k-1], *it) < 0) k--;//allow collinear points!
+ h[k++] = *it;
+ }};
+ half(all(pts), 1);// Untere Hülle.
+ half(next(pts.rbegin()), pts.rend(), k);// Obere Hülle.
+ h.resize(k);
+ return h;
+}
+
+lll area(const vector<pt>& poly) { //poly[0] == poly.back()
+ lll res = 0;
+ for (int i = 0; i + 1 < sz(poly); i++)
+ res += cross(poly[i], poly[i + 1]);
+ return res;
+}
+
+// Liegt p auf der Strecke a-b?
+bool pointInLineSegment(pt a, pt b, pt p) {
+ if (cross(a, b, p) != 0) return false;
+ auto dist = norm(a - b);
+ return norm(a - p) < dist && norm(b - p) < dist;
+}
+
+// Test auf Streckenschnitt zwischen a-b und c-d.
+// (nur intern)
+bool lineSegmentIntersection(pt a, pt b, pt c, pt d) {
+ if (cross(a, b, c) == 0 && cross(a, b, d) == 0) {
+ return pointInLineSegment(a,b,c) ||
+ pointInLineSegment(a,b,d) ||
+ pointInLineSegment(c,d,a) ||
+ pointInLineSegment(c,d,b);
+ }
+ return cross(a, b, c) * cross(a, b, d) < 0 &&
+ cross(c, d, a) * cross(c, d, b) < 0;
+}
+
+// 1 => c links von a->b
+// 0 => a, b und c kolliniear
+// -1 => c rechts von a->b
+int ccw(pt a, pt b, pt c) {
+ auto orien = cross(b - a, c - a);
+ return (orien > 0) - (orien < 0);
+}
+
+bool inOutCirc(pt a, pt b, pt c, pt p) {
+ lll p2 = norm(p);
+ lll A = norm(a)-p2;
+ lll B = norm(b)-p2;
+ lll C = norm(c)-p2;
+ return ccw(a, b, c) * (cross(p, a, b)*C + cross(p, b, c)*A + cross(p, c, a)*B) > 0;
+}
+
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(3, 30);
+ auto ps = Random::points<lll>(n, -range, range);
+ bool skip = true;
+ for (int i = 2; i < n; i++) skip &= cross(ps[i-2], ps[i-1], ps[i]) == 0;
+ if (skip) continue;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < i; j++) {
+ skip |= ps[i] == ps[j];
+ }
+ }
+ if (skip) continue;
+
+ auto hull = convexHull(ps);
+ lll expectedArea = area(hull);
+ hull.pop_back();
+
+ auto got = delaunay(ps);
+ if (sz(got) % 3 != 0) cerr << "error: not triangles" << FAIL;
+ if (sz(got) / 3 + sz(hull) - 3 + 1 != 2 * sz(ps) - 4) cerr << "error: wrong number" << FAIL;
+
+ //all triangles should be oriented ccw
+ lll gotArea = 0;
+ for (int i = 0; i < sz(got); i += 3) gotArea += cross(got[i], got[i+1], got[i+2]);
+ if (gotArea != expectedArea) cerr << "error: wrong area" << FAIL;
+
+ for (int i = 0; i < sz(got); i++) {
+ int ii = i + 1;
+ if (i / 3 != ii / 3) ii -= 3;
+ for (int j = 0; j < i; j++) {
+ int jj = j + 1;
+ if (j / 3 != jj / 3) jj -= 3;
+
+ if (got[i] == got[j] && got[ii] == got[jj]) cerr << "error: dublicate" << FAIL;
+ if (lineSegmentIntersection(got[i], got[ii], got[j], got[jj])) cerr << "error: intersection" << FAIL;
+ }
+ bool seen = false;
+ for (pt p : ps) seen |= p == got[i];
+ if (!seen) cerr << "error: invalid point" << FAIL;
+ }
+ for (int i = 0; i < sz(got); i += 3) {
+ for (pt p : ps) {
+ if (p == got[i]) continue;
+ if (p == got[i+1]) continue;
+ if (p == got[i+2]) continue;
+ if (inOutCirc(got[i], got[i+1], got[i+2], p)) cerr << "error: not delaunay" << FAIL;
+ }
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 100'000;
+void performance_test() {
+ timer t;
+ auto ps = Random::points<lll>(N, -1'000'000'000, 1'000'000'000);
+ t.start();
+ auto got = delaunay(ps);
+ t.stop();
+ hash_t hash = sz(got);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(10);
+ stress_test(10'000);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/formulas.cpp b/test/geometry/formulas.cpp
new file mode 100644
index 0000000..d63d431
--- /dev/null
+++ b/test/geometry/formulas.cpp
@@ -0,0 +1,127 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+
+void test_dot(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto p = Random::point<ll>(-range, range);
+ auto q = Random::point<ll>(-range, range);
+
+ ll expected = real(p) * real(q) + imag(p) * imag(q);
+ ll got = dot(p, q);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested dot: " << queries << endl;
+}
+
+void test_norm(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto p = Random::point<ll>(-range, range);
+
+ ll expected = real(p) * real(p) + imag(p) * imag(p);
+ ll got = norm(p);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested norm: " << queries << endl;
+}
+
+void test_cross(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto p = Random::point<ll>(-range, range);
+ auto q = Random::point<ll>(-range, range);
+
+ ll expected = real(p) * imag(q) - imag(p) * real(q);
+ ll got = cross(p, q);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested cross1: " << queries << endl;
+
+ queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto a = Random::point<ll>(-range, range);
+ auto b = Random::point<ll>(-range, range);
+ auto c = Random::point<ll>(-range, range);
+
+ ll expected = cross(b - a, c - a);
+ ll got = cross(a, b, c);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested cross2: " << queries << endl;
+}
+
+void test_ccw(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto a = Random::point<ll>(-range, range);
+ auto b = Random::point<ll>(-range, range);
+ auto c = Random::point<ll>(-range, range);
+
+ ll expected = cross(a, b, c);
+ if (expected < 0) expected = -1;
+ if (expected > 0) expected = 1;
+ ll got = ccw(a, b, c);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested ccw: " << queries << endl;
+}
+
+void test_isCoplanar(ll range) {(void) range;}// cant check this...
+
+void test_uniqueAngle(ll range) {
+ auto lessPt = [](pt a, pt b){
+ if (real(a) != real(b)) return real(a) < real(b);
+ return imag(a) < imag(b);
+ };
+ map<pt, pt, decltype(lessPt)> seen(lessPt);
+
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ pt expected = Random::point<ll>(-sqrt(range), sqrt(range));
+ ll g = abs(gcd(real(expected), imag(expected)));
+ if (g == 0) continue;
+ expected /= g;
+
+ pt rot = Random::point<ll>(-sqrt(range), sqrt(range));
+ if (norm(rot) == 0) continue;
+
+ pt got = uniqueAngle(expected * rot, pt(Random::integer<ll>(1, sqrt(range)), 0) * rot);
+ auto it = seen.emplace(got, expected).first;
+
+ if (it->second != expected) cerr << "error: inconsistent" << FAIL;
+ queries++;
+ }
+ cerr << "tested uniqueAngle: " << queries << " (" << sz(seen) << ")" << endl;
+}
+
+int main() {
+ test_dot(100);
+ test_dot(1'000'000'000);
+ test_norm(100);
+ test_norm(1'000'000'000);
+ test_cross(100);
+ test_cross(1'000'000'000);
+ test_ccw(100);
+ test_ccw(1'000'000'000);
+ test_isCoplanar(100);
+ test_isCoplanar(1'000'000'000);
+ test_uniqueAngle(100);
+ test_uniqueAngle(10'000);
+ test_uniqueAngle(1'000'000'000);
+}
diff --git a/test/geometry/linesAndSegments.cpp b/test/geometry/linesAndSegments.cpp
new file mode 100644
index 0000000..2943a67
--- /dev/null
+++ b/test/geometry/linesAndSegments.cpp
@@ -0,0 +1,240 @@
+#include "../util.h"
+constexpr double EPS = 1e-9;
+#define ll double
+double gcd(double x, double /**/) {return x;} //hacky
+#include <geometry/formulas.cpp>
+#undef ll
+#include <geometry/linesAndSegments.cpp>
+
+#include "../geometry.h"
+
+void stress_pointOnLine(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ pt p = Random::integerPoint(range);
+
+ bool expected = ccw(a, b, p) == 0;
+ bool got = pointOnLine(a, b, p);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested pointOnLine: " << queries << endl;
+}
+
+void stress_lineIntersection(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ auto [c, d] = Random::line(range);
+ if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) continue;
+
+ bool expected = ccw(0, a-b, c-d) == 0;
+ bool got = lineIntersection(a, b, c, d);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested lineIntersection: " << queries << endl;
+}
+
+void stress_lineIntersection2(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ auto [c, d] = Random::line(range);
+ if (ccw(0, a-b, c-d) == 0) continue;
+
+ auto got = lineIntersection2(a, b, c, d);
+
+ if (distToLine(a, b, got) > 1e-6) cerr << "error: 1" << FAIL;
+ if (distToLine(a, b, got) > 1e-6) cerr << "error: 2" << FAIL;
+ queries++;
+ }
+ cerr << "tested lineIntersection2: " << queries << endl;
+}
+
+void stress_distToLine(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ pt p = Random::integerPoint(range);
+
+ auto got = distToLine(a, b, p);
+ auto expected = abs(p - projectToLine(a, b, p));
+
+ if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL;
+
+ queries++;
+ }
+ cerr << "tested distToLine: " << queries << endl;
+}
+
+void stress_projectToLine(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ pt p = Random::integerPoint(range);
+
+ auto got = projectToLine(a, b, p);
+
+ if (distToLine(a, b, got) > 1e-6) cerr << "error: 1" << FAIL;
+ if (dot((b-a)/abs(b-a), (got-p)/abs(got-p)) > 1e-6) cerr << "error: 2" << FAIL;
+
+ queries++;
+ }
+ cerr << "tested projectToLine: " << queries << endl;
+}
+
+void stress_sortLine(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ pt dir = 0;
+ while (norm(dir) == 0) dir = Random::integerPoint(range);
+ int n = Random::integer<int>(1, 30);
+ vector<pt> ps = Random::integerPoints(n, range);
+
+ sortLine(dir, ps);
+
+ for (int i = 1; i < n; i++) {
+ if (dot(dir, ps[i-1]) > dot(dir, ps[i])) cerr << "error" << FAIL;
+ }
+ queries+=n;
+ }
+ cerr << "tested sortLine: " << queries << endl;
+}
+
+void stress_pointOnSegment(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ pt p = Random::integerPoint(range);
+
+ bool expected = pointOnLine(a, b, p) && abs(a-p) <= abs(a-b) && abs(b-p) <= abs(a-b);
+ bool got = pointOnSegment(a, b, p);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested pointOnSegment: " << queries << endl;
+}
+
+void stress_distToSegment(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ pt p = Random::integerPoint(range);
+
+ double expected = min(abs(p-a), abs(p-b));
+ if (dot(b-a,p-a) >= 0 && dot(a-b,p-b) >= 0) expected = min(expected, distToLine(a, b, p));
+ double got = distToSegment(a, b, p);
+
+ if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested distToSegment: " << queries << endl;
+}
+
+void stress_segmentIntersection(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ auto [c, d] = Random::line(range);
+
+ bool expected;
+ if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0) {
+ expected = pointOnSegment(a,b,c) ||
+ pointOnSegment(a,b,d) ||
+ pointOnSegment(c,d,a) ||
+ pointOnSegment(c,d,b);
+ } else {
+ expected = ccw(a, b, c) * ccw(a, b, d) <= 0 &&
+ ccw(c, d, a) * ccw(c, d, b) <= 0;
+ }
+ bool got = segmentIntersection(a, b, c, d);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested segmentIntersection: " << queries << endl;
+}
+
+void stress_segmentIntersection2(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ auto [c, d] = Random::line(range);
+
+ auto got = segmentIntersection2(a, b, c, d);
+ auto tmp = segmentIntersection(a, b, c, d);
+
+ if (!got.empty() != tmp) cerr << "error: 1" << FAIL;
+ for (pt p : got) {
+ if (distToSegment(a, b, p) > 1e-6) cerr << "error: 2" << FAIL;
+ if (distToSegment(a, b, p) > 1e-6) cerr << "error: 3" << FAIL;
+ }
+ if (tmp) {
+ double gotDist = abs(got.front() - got.back());
+ double expectedDist = 0;
+ array<pt, 4> tmp2 = {a, b, c, d};
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < i; j++) {
+ if (!pointOnSegment(a, b, tmp2[i])) continue;
+ if (!pointOnSegment(c, d, tmp2[i])) continue;
+ if (!pointOnSegment(a, b, tmp2[j])) continue;
+ if (!pointOnSegment(c, d, tmp2[j])) continue;
+ expectedDist = max(expectedDist, abs(tmp2[i] - tmp2[j]));
+ }
+ }
+ if (float_error(gotDist, expectedDist) > 1e-6) cerr << "error: 4" << FAIL;
+ }
+ queries++;
+ }
+ cerr << "tested segmentIntersection2: " << queries << endl;
+}
+
+void stress_distBetweenSegments(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ auto [a, b] = Random::line(range);
+ auto [c, d] = Random::line(range);
+
+ double expected = 0;
+ if (!segmentIntersection(a, b, c, d)) {
+ expected = min({distToSegment(a, b, c), distToSegment(a, b, d),
+ distToSegment(c, d, a), distToSegment(c, d, b)});
+ }
+ double got = distBetweenSegments(a, b, c, d);
+
+ if (float_error(got, expected) > 1e-6) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested distBetweenSegments: " << queries << endl;
+}
+
+int main() {
+ stress_pointOnLine(100);
+ stress_pointOnLine(10'000);
+ stress_pointOnLine(1'000'000'000);
+ stress_lineIntersection(100);
+ stress_lineIntersection(1'000'000'000);
+ stress_lineIntersection2(100);
+ stress_lineIntersection2(1'000'000);
+ stress_distToLine(100);
+ stress_distToLine(1'000'000'000);
+ stress_projectToLine(100);
+ stress_projectToLine(1'000'000);
+ stress_sortLine(100);
+ stress_sortLine(1'000'000'000);
+ stress_pointOnSegment(100);
+ stress_pointOnSegment(1'000'000'000);
+ stress_distToSegment(100);
+ stress_distToSegment(1'000'000'000);
+ stress_segmentIntersection(100);
+ stress_segmentIntersection(1'000'000'000);
+ stress_segmentIntersection2(100);
+ stress_segmentIntersection2(1'000'000'000);
+ stress_distBetweenSegments(100);
+ stress_distBetweenSegments(1'000'000'000);
+}
diff --git a/test/geometry/polygon.cpp b/test/geometry/polygon.cpp
new file mode 100644
index 0000000..1dd46ca
--- /dev/null
+++ b/test/geometry/polygon.cpp
@@ -0,0 +1,296 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+constexpr double INF = LD::INF;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+double abs(pt p) {
+ return hypot(real(p), imag(p));
+}
+// Liegt p auf der Strecke a-b?
+bool pointOnLineSegment(pt a, pt b, pt p) {
+ if (cross(a, b, p) != 0) return false;
+ auto dist = norm(a - b);
+ return norm(a - p) <= dist && norm(b - p) <= dist;
+}
+// Entfernung von Punkt p zur Strecke a-b.
+double distToSegment(pt a, pt b, pt p) {
+ if (a == b) return abs(p - a);
+ if (dot(p - a, b - a) <= 0) return abs(p - a);
+ if (dot(p - b, b - a) >= 0) return abs(p - b);
+ return abs(cross(p - a, b - a)) / abs(b - a);
+}
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#include <geometry/polygon.cpp>
+#include "../geometry.h"
+
+void test_area(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 30);
+ auto ps = Random::polygon(n, range);
+ ps.push_back(ps[0]);
+
+ ll expected = 0;
+ for (int i = 0; i < n; i++) {
+ expected += cross(0, ps[i], ps[i+1]);
+ }
+ double got = area(ps) * 2;
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested area: " << queries << endl;
+}
+
+bool ptLess(pt a, pt b) {
+ if (real(a) != real(b)) return real(a) < real(b);
+ return imag(a) < imag(b);
+}
+
+void test_windingNumber(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 8);
+ auto ps = Random::polygon(n, range);
+ ps.push_back(ps[0]);
+
+ for (int i = 0; i < 100; i++) {
+ auto p = Random::point<ll>(-range, range);
+
+ ll expected = 0;
+ bool onBorder = false;
+ for (int j = 0; j < n; j++) {
+ int cur = details::lineSegmentIntersection(p, p + pt(1, 2'000'000'007), ps[j], ps[j+1]);
+ if (ptLess(ps[j], ps[j+1])) expected -= cur;
+ else expected += cur;
+ onBorder |= pointOnLineSegment(ps[j], ps[j+1], p);
+ }
+ if (onBorder) continue;
+ if (area(ps) < 0) expected = -expected;
+
+ bool got = windingNumber(p, ps);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested windingNumber: " << queries << endl;
+}
+
+void test_inside(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 30);
+ auto ps = Random::polygon(n, range);
+ ps.push_back(ps[0]);
+
+ for (int i = 0; i < 100; i++) {
+ auto p = Random::point<ll>(-range, range);
+
+ ll count = 0;
+ bool onBorder = false;
+ for (int j = 0; j < n; j++) {
+ count += details::lineSegmentIntersection(p, p + pt(1, 2'000'000'007), ps[j], ps[j+1]);
+ onBorder |= pointOnLineSegment(ps[j], ps[j+1], p);
+ }
+ bool expected = (count % 2) && !onBorder;
+ bool got = inside(p, ps);
+
+ if (got != expected) cerr << "error" << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested inside: " << queries << endl;
+}
+
+void test_insideConvex(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 30);
+ auto ps = Random::convex(n, range);
+
+ for (int i = 0; i < 100; i++) {
+ auto p = Random::point<ll>(-range, range);
+
+ bool expected = true;
+ for (int j = 0; j < n; j++) expected &= cross(p, ps[j], ps[(j+1) % n]) > 0;
+
+ bool got = insideConvex(p, ps);
+
+ if (got != expected) {
+ for (pt pp : ps) cerr << pp << " ";
+ cerr << endl;
+ cerr << p << endl;
+ }
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested insideConvex: " << queries << endl;
+}
+
+// convex hull without duplicates, h[0] != h.back()
+// apply comments if border counts as inside
+bool insideOrOnConvex(pt p, const vector<pt>& hull) {
+ int l = 0, r = sz(hull) - 1;
+ if (cross(hull[0], hull[r], p) > 0) return false;
+ while (l + 1 < r) {
+ int m = (l + r) / 2;
+ if (cross(hull[0], hull[m], p) >= 0) l = m;
+ else r = m;
+ }
+ return cross(hull[l], hull[r], p) >= 0;
+}
+
+void test_minkowski(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 30);
+ auto A = Random::convex(n, range);
+ int m = Random::integer(3, 30);
+ auto B = Random::convex(n, range);
+
+ auto got = minkowski(A, B);
+ bool convex = true;
+ for (int i = 0; i < sz(got); i++) convex &= cross(got[i], got[(i+1) % sz(got)], got[(i+2) % sz(got)]) >= 0;
+ if (!convex) cerr << "error: not convex" << FAIL;
+
+ for (pt a : A) {
+ for (pt b : B) {
+ if (!insideOrOnConvex(a + b, got)) cerr << "error: not sum" << FAIL;
+ }
+ }
+ queries += n + m;
+ }
+ cerr << "tested minkowski: " << queries << endl;
+}
+
+double naive_dist(const vector<pt>& ps, const vector<pt>& qs) {
+ //check if intersect
+ double res = LD::INF;
+ bool intersect = true;
+ for (int i = 0; i < sz(qs); i++) {
+ bool sep = true;
+ for (pt p : ps) {
+ res = min(res, distToSegment(qs[i], qs[(i+1) % sz(qs)], p));
+ sep &= cross(qs[i], qs[(i+1) % sz(qs)], p) <= 0;
+ }
+ if (sep) intersect = false;
+ }
+ for (int i = 0; i < sz(ps); i++) {
+ bool sep = true;
+ for (pt q : qs) {
+ res = min(res, distToSegment(ps[i], ps[(i+1) % sz(ps)], q));
+ sep &= cross(ps[i], ps[(i+1) % sz(ps)], q) <= 0;
+ }
+ if (sep) intersect = false;
+ }
+ if (intersect) return 0;
+ return res;
+}
+
+void test_dist(ll range) {
+ int queries = 0;
+ int pos = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 10);
+ auto A = Random::convex(n, range / 3);
+ int m = Random::integer(3, 10);
+ auto B = Random::convex(n, range / 3);
+
+ pt offset = Random::point<ll>(range / 3, 2 * range / 3);
+ for (pt& p : B) p += offset;
+
+ auto got = dist(A, B);
+ auto expected = naive_dist(A, B);
+
+ if (float_error(got, expected) > 1e-6) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ if (got > 0) pos++;
+
+ queries += n + m;
+ }
+ cerr << "tested dist: " << queries << " (" << pos << ")" << endl;
+}
+
+void test_extremal(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 30);
+ auto ps = Random::convex(n, range);
+ ps.push_back(ps[0]);
+
+ for (int i = 0; i < 100; i++) {
+ auto dir = Random::point<ll>(-range, range);
+ int tmp = extremal(ps, dir);
+ if (tmp < 0 || tmp >= n) cerr << "error: out of range" << FAIL;
+
+ auto got = ps[tmp];
+ bool extremal = true;
+ for (pt p : ps) extremal &= dot(dir, p) <= dot(dir, got);
+
+ if (!extremal) cerr << "error: not extremal" << FAIL;
+ queries += n;
+ }
+ }
+ cerr << "tested extremal: " << queries << endl;
+}
+
+void test_intersect(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer(3, 10);
+ auto ps = Random::convex(n, range);
+ ps.push_back(ps[0]);
+
+ for (int i = 0; i < 100; i++) {
+ pt a = Random::point<ll>(-range, range);
+ pt b = a;
+ while (b == a) b = Random::point<ll>(-range, range);
+
+ auto got = intersectLine(ps, a, b);
+
+ vector<int> expected;
+ for (int j = 0; j < n; j++) {
+ if (cross(ps[j], a, b) > 0 && cross(ps[j+1], a, b) < 0) expected.push_back(j);
+ if (cross(ps[j], a, b) < 0 && cross(ps[j+1], a, b) > 0) expected.push_back(j);
+ if (cross(ps[j], a, b) == 0) {
+ if (cross(ps[j+1], a, b) != 0 ||
+ cross(ps[(j+n-1) % n], a, b) != 0) {
+ expected.push_back(j);
+ }
+ }
+ }
+ if (sz(expected) > 1 && expected[0] == expected[1]) expected.pop_back();
+
+ sort(all(got));
+ sort(all(expected));
+
+ if (got != expected) cerr << "error" << FAIL;
+
+ queries += n;
+ }
+ }
+ cerr << "tested intersect: " << queries << endl;
+}
+
+int main() {
+ test_area(100);
+ test_area(1'000'000'000);
+ test_windingNumber(100);
+ test_windingNumber(1'000'000'000);
+ test_inside(100);
+ test_inside(1'000'000'000);
+ test_insideConvex(100);
+ test_insideConvex(1'000'000'000);
+ test_minkowski(100);
+ test_minkowski(500'000'000);
+ test_dist(100);
+ test_dist(1'000'000'000);
+ test_extremal(100);
+ test_extremal(1'000'000'000);
+ test_intersect(100);
+ test_intersect(1'000'000'000);
+}
diff --git a/test/geometry/segmentIntersection.cpp b/test/geometry/segmentIntersection.cpp
new file mode 100644
index 0000000..9862be5
--- /dev/null
+++ b/test/geometry/segmentIntersection.cpp
@@ -0,0 +1,88 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+
+// Liegt p auf der Strecke a-b?
+bool pointOnLineSegment(pt a, pt b, pt p) {
+ if (cross(a, b, p) != 0) return false;
+ double dist = norm(a - b);
+ return norm(a - p) <= dist && norm(b - p) <= dist;
+}
+
+// Test auf Streckenschnitt zwischen a-b und c-d.
+bool lineSegmentIntersection(pt a, pt b, pt c, pt d) {
+ if (ccw(a, b, c) == 0 && ccw(a, b, d) == 0)
+ return pointOnLineSegment(a,b,c) ||
+ pointOnLineSegment(a,b,d) ||
+ pointOnLineSegment(c,d,a) ||
+ pointOnLineSegment(c,d,b);
+ return ccw(a, b, c) * ccw(a, b, d) <= 0 &&
+ ccw(c, d, a) * ccw(c, d, b) <= 0;
+}
+
+#include <geometry/segmentIntersection.cpp>
+
+vector<seg> randomSegs(int n, ll range) {
+ auto ps = Random::points<ll>(n, -range, range);
+ vector<seg> segs(n);
+ for (int i = 0; i < n; i++) {
+ pt b;
+ do {
+ b = Random::point<ll>(-pow(range, 0.8), pow(range, 0.8));
+ } while(norm(b) == 0);
+ segs[i] = {ps[i], ps[i] + b, i};
+ }
+ return segs;
+}
+
+bool naive(vector<seg>& segs) {
+ for (ll i = 0; i < sz(segs); i++) {
+ for (ll j = 0; j < i; j++) {
+ if (lineSegmentIntersection(segs[i].a, segs[i].b, segs[j].a, segs[j].b)) return true;
+ }
+ }
+ return false;
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ ll intersection = 0;
+ ll notIntersection = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 100);
+ auto segs = randomSegs(n, range);
+ auto [a, b] = intersect(segs);
+ bool got = a >= 0;
+ if (got != (b >= 0)) cerr << "error: invalid ans" << FAIL;
+ auto expected = naive(segs);
+ if (got != expected) cerr << "error: intersection not found" << FAIL;
+ if (got && !lineSegmentIntersection(segs[a].a, segs[a].b, segs[b].a, segs[b].b)) cerr << "error: no intersection" << FAIL;
+ queries += n;
+ intersection += got;
+ notIntersection += !got;
+ }
+ cerr << "tested random queries: " << queries << "(" << intersection << ":" << notIntersection << ")" << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ auto segs = randomSegs(N, 1'000'000'000);
+
+ t.start();
+ hash_t hash = intersect(segs).first;
+ t.stop();
+
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/sortAround.cpp b/test/geometry/sortAround.cpp
new file mode 100644
index 0000000..a27edc8
--- /dev/null
+++ b/test/geometry/sortAround.cpp
@@ -0,0 +1,83 @@
+#include "../util.h"
+constexpr ll EPS = 0;
+#define double ll
+#define polar polar<ll>
+#include <geometry/formulas.cpp>
+#undef polar
+#undef double
+#include <geometry/sortAround.cpp>
+
+//expected order:
+//1 8 7
+//2 . 6
+//3 4 5
+void test_tiny() {
+ vector<pt> expected = {
+ {-1, 1},
+ {-1, 0},
+ {-1,-1},
+ { 0,-1},
+ { 1,-1},
+ { 1, 0},
+ { 1, 1},
+ { 0, 1},
+ };
+ auto got = expected;
+ for (int i = 0; i < 100'000; i++) {
+ shuffle(all(got), Random::rng);
+ sortAround(0, got);
+ if (got != expected) cerr << "error" << FAIL;
+ }
+ cerr << "tested tiny" << endl;
+}
+
+void stress_test(ll range) {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 100);
+ auto ps = Random::points<ll>(n, -range, range);
+
+ auto contains = [&](pt p){
+ for (pt pp : ps) if (pp == p) return true;
+ return false;
+ };
+
+ pt c;
+ do {
+ c = Random::point<ll>(-range, range);
+ } while (contains(c));
+
+ sortAround(c, ps);
+
+ auto isLeft = [&](pt p){return real(p - c) < 0 || (real(p - c) == 0 && imag(p - c) < 0);};
+ auto isCCW = [&](pt a, pt b){return cross(c, a, b) > 0;};
+ if (!is_partitioned(all(ps), isLeft)) cerr << "error 1" << FAIL;
+ auto mid = partition_point(all(ps), isLeft);
+ if (!is_sorted(ps.begin(), mid, isCCW)) cerr << "error 2" << FAIL;
+ if (!is_sorted(mid, ps.end(), isCCW)) cerr << "error 3" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 2'000'000;
+void performance_test() {
+ timer t;
+ auto ps = Random::points<ll>(N, -1'000'000'000, 1'000'000'000);
+
+ t.start();
+ sortAround(0, ps);
+ t.stop();
+
+ hash_t hash = 0;
+ for (pt p : ps) hash += real(p) * imag(p);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ test_tiny();
+ stress_test(100);
+ stress_test(1'000'000'000);
+ performance_test();
+}
diff --git a/test/geometry/triangle.cpp b/test/geometry/triangle.cpp
new file mode 100644
index 0000000..dc620ee
--- /dev/null
+++ b/test/geometry/triangle.cpp
@@ -0,0 +1,146 @@
+#include "../util.h"
+constexpr double EPS = 1e-6;
+#define ll double
+double gcd(double x, double /**/) {return x;} //hacky
+#include <geometry/formulas.cpp>
+#undef ll
+ll sgn(double x) {
+ return (x > EPS) - (x < -EPS);
+}
+#include <geometry/triangle.cpp>
+#include "../geometry.h"
+
+// Entfernung von Punkt p zur Geraden durch a-b. 2d und 3d
+double distToLine(pt a, pt b, pt p) {
+ return abs(cross(p - a, b - a)) / abs(b - a);
+}
+
+void test_centroid(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(range);
+
+ pt center = centroid(a, b, c);
+
+ if (distToLine(2.0*a, c+b, 2.0*center) > 1e-6) cerr << "error: 1" << FAIL;
+ if (distToLine(2.0*b, c+a, 2.0*center) > 1e-6) cerr << "error: 2" << FAIL;
+ if (distToLine(2.0*c, a+b, 2.0*center) > 1e-6) cerr << "error: 3" << FAIL;
+ queries++;
+ }
+ cerr << "tested centroid: " << queries << endl;
+}
+
+void test_area(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(range);
+
+ auto gotA = 2*area(a, b, c);
+ auto gotB = 2*area(abs(a-b), abs(b-c), abs(c-a));
+ auto expected = llround(gotA);
+
+ if (float_error(gotA, expected) > 1e-6) cerr << "error: 1" << FAIL;
+ if (float_error(gotB, expected) > 1e-3) cerr << "error: 2" << FAIL;
+ queries++;
+ }
+ cerr << "tested area: " << queries << endl;
+}
+
+void test_inCenter(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(range);
+
+ pt center = inCenter(a, b, c);
+
+ double da = distToLine(a, b, center);
+ double db = distToLine(b, c, center);
+ double dc = distToLine(c, a, center);
+
+ double avg = (da + db + dc) / 3.0;
+
+ if (float_error(da, avg) > 1e-6) cerr << "error: 1" << FAIL;
+ if (float_error(db, avg) > 1e-6) cerr << "error: 2" << FAIL;
+ if (float_error(dc, avg) > 1e-6) cerr << "error: 3" << FAIL;
+ queries++;
+ }
+ cerr << "tested inCenter: " << queries << endl;
+}
+
+void test_circumCenter(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(range);
+
+ pt center = circumCenter(a, b, c);
+
+ double da = abs(center - a);
+ double db = abs(center - b);
+ double dc = abs(center - c);
+
+ double avg = (da + db + dc) / 3.0;
+
+ if (float_error(da, avg) > 1e-6) cerr << "error: 1" << FAIL;
+ if (float_error(db, avg) > 1e-6) cerr << "error: 2" << FAIL;
+ if (float_error(dc, avg) > 1e-6) cerr << "error: 3" << FAIL;
+ queries++;
+ }
+ cerr << "tested circumCenter: " << queries << endl;
+}
+
+void test_insideOutCenter(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(range);
+ pt p = Random::integerPoint(range);
+
+ pt center = circumCenter(a, b, c);
+
+ double da = abs(center - a);
+ double db = abs(center - b);
+ double dc = abs(center - c);
+ double dp = abs(center - p);
+
+ double avg = (da + db + dc) / 3.0;
+
+ int expected = dp < avg ? 1 : -1;
+ if (float_error(dp, avg) < 1e-9) expected = 0;
+
+ if (insideOutCenter(a, b, c, p) != expected) cerr << "error" << FAIL;
+
+ queries++;
+ }
+ cerr << "tested insideOutCenter: " << queries << endl;
+}
+
+void test_similar(ll range) {
+ int queries = 0;
+ for (int tries = 0; tries < 1'000'000; tries++) {
+ auto [a, b, c] = Random::triangle(sqrt(range));
+ pt rot = Random::integerPoint(sqrt(range));
+ pt add = Random::integerPoint(range);
+
+ pt d = rot * a + add;
+ pt e = rot * b + add;
+ pt f = rot * c + add;
+
+ if (!similar(a, b, c, d, e, f)) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested similar: " << queries << endl;
+}
+
+int main() {
+ test_centroid(100);
+ test_centroid(1'000'000'000);
+ test_area(100);
+ test_area(1'000'000'000);
+ test_inCenter(100);
+ test_inCenter(1'000'000'000);
+ test_circumCenter(100);
+ test_circumCenter(1'000'000'000);
+ test_insideOutCenter(100);
+ test_insideOutCenter(1'000'000'000);
+ test_similar(100);
+ test_similar(1'000'000'000);
+}
diff --git a/test/graph/2sat.cpp b/test/graph/2sat.cpp
new file mode 100644
index 0000000..fc3186e
--- /dev/null
+++ b/test/graph/2sat.cpp
@@ -0,0 +1,133 @@
+#include "../util.h"
+#include <graph/scc.cpp>
+#define static vector<vector<int>> adj; static // hacky...
+#include <graph/2sat.cpp>
+#undef static
+#undef adj
+
+struct RandomClause {
+ int a, b;
+ int type;
+ RandomClause(int n) :
+ a(Random::integer<int>(0, 2*n)),
+ b(Random::integer<int>(0, 2*n)),
+ type(Random::integer<int>(0, 8)) {}
+
+ bool eval(vector<int>& sol) const {
+ bool ba = sol[a];
+ bool bb = sol[b];
+ if (type == 0) return !ba || bb;
+ if (type == 1) return ba == bb;
+ if (type == 2) return ba || bb;
+ if (type == 3) return ba != bb;
+ if (type == 4) return ba && bb;
+ if (type == 5) return !(ba && bb);
+
+ if (type == 6) return ba;
+ if (type == 7) return !ba;
+ return false;
+ }
+
+ void add(sat2& sat) const {
+ int va = a;
+ int vb = b;
+ if (type == 0) sat.addImpl(va, vb);
+ if (type == 1) sat.addEquiv(va, vb);
+ if (type == 2) sat.addOr(va, vb);
+ if (type == 3) sat.addXor(va, vb);
+ if (type == 4) sat.addAnd(va, vb);
+ if (type == 5) sat.addNand(va, vb);
+
+ if (type == 6) sat.addTrue(va);
+ if (type == 7) sat.addFalse(va);
+ }
+
+ friend ostream& operator<<(ostream& os, const RandomClause& c) {
+ if (c.a & 1) os << "-";
+ os << (c.a >> 1);
+ if (c.type == 0) os << "=>";
+ if (c.type == 1) os << "==";
+ if (c.type == 2) os << "or";
+ if (c.type == 3) os << "xor";
+ if (c.type == 4) os << "and";
+ if (c.type == 5) os << "nand";
+
+ if (c.type == 6) return os;
+ if (c.type == 7) return os << "==F";
+
+ if (c.b & 1) os << "-";
+ os << (c.b >> 1);
+ return os;
+ }
+};
+
+bool naive(int n, const vector<RandomClause>& clauses) {
+ for (ll i = 0; i < (1ll << n); i++) {
+ vector<int> tmp(2*n);
+ for (ll j = 0; j < n; j++) {
+ tmp[(2*j) + ((i >> j) & 1)] = 1;
+ }
+ bool ok = true;
+ for (auto& c : clauses) ok &= c.eval(tmp);
+ if (ok) return true;
+ }
+ return false;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 12);
+ int m = Random::integer<int>(0, 30);
+
+ vector<RandomClause> clauses;
+ for (int i = 0; i < m; i++) clauses.emplace_back(n);
+
+ sat2 sat(n);
+ for (auto& c : clauses) c.add(sat);
+ adj = sat.adj;
+
+ bool got = sat.solve();
+ bool expected = naive(n, clauses);
+
+ if (got) {
+ for (int i = 0; i < 2*n; i+=2) {
+ if (sat.sol[i] < 0) cerr << "error: invalid vars" << FAIL;
+ if (sat.sol[i+1] < 0) cerr << "error: invalid vars" << FAIL;
+ if (sat.sol[i] == sat.sol[i+1]) cerr << "error: inconsistent vars" << FAIL;
+ }
+ for (auto& c : clauses) {
+ if (!c.eval(sat.sol)) {
+ cerr << "error: inconsistent" << FAIL;
+ }
+ }
+ }
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 200'000;
+constexpr int M = 500'000;
+void performance_test() {
+ timer t;
+ vector<RandomClause> clauses;
+ for (int i = 0; i < M; i++) clauses.emplace_back(N);
+ t.start();
+ sat2 sat(N);
+ for (auto& c : clauses) c.add(sat);
+ t.stop();
+ adj = sat.adj;
+ t.start();
+ hash_t hash = sat.solve();
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/LCA_sparse.cpp b/test/graph/LCA_sparse.cpp
new file mode 100644
index 0000000..f6eb345
--- /dev/null
+++ b/test/graph/LCA_sparse.cpp
@@ -0,0 +1,63 @@
+#include "../util.h"
+#include <datastructures/sparseTable.cpp>
+#include <graph/LCA_sparse.cpp>
+namespace expected {
+#include <graph/hld.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ Graph<NoData> g(n);
+ g.tree();
+
+ vector<vector<int>> adj(n);
+ g.forEdges([&](int a, int b){
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ });
+
+ LCA lca;
+ lca.init(adj, 0);
+
+ expected::adj = adj;
+ expected::init();
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j <= i; j++) {
+ auto got = lca.getLCA(i, j);
+ auto expected = expected::get_lca(i, j);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.tree();
+ vector<vector<int>> adj(N);
+ g.forEdges([&](int a, int b){
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ });
+
+ hash_t hash = 0;
+ t.start();
+ LCA lca;
+ lca.init(adj, 0);
+ for (int i = 1; i < N; i++) hash += lca.getLCA(i-1, i);
+ t.stop();
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/TSP.cpp b/test/graph/TSP.cpp
new file mode 100644
index 0000000..f9aab2e
--- /dev/null
+++ b/test/graph/TSP.cpp
@@ -0,0 +1,67 @@
+#include "../util.h"
+struct edge {
+ ll dist;
+ int to;
+};
+constexpr ll INF = LL::INF;
+#include <graph/TSP.cpp>
+
+vector<int> naive() {
+ int n = sz(dist);
+ vector<int> todo(n - 1);
+ iota(all(todo), 1);
+ vector<int> res;
+ ll best = LL::INF;
+ do {
+ int last = 0;
+ ll cur = 0;
+ for (int x : todo) {
+ cur += dist[last][x];
+ last = x;
+ }
+ cur += dist[last][0];
+ if (cur < best) {
+ best = cur;
+ res = todo;
+ res.insert(res.begin(), 0);
+ res.push_back(0);
+ }
+ } while (next_permutation(all(todo)));
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 100'000; i++) {
+ int n = Random::integer<int>(1, 9);
+ dist.assign(n, {});
+ for (auto& v : dist) v = Random::integers<ll>(n, 0, 1000'000'000);
+
+ auto expected = naive();
+ auto got = TSP();
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 19;
+void performance_test() {
+ timer t;
+ dist.assign(N, {});
+ for (auto& v : dist) v = Random::integers<ll>(N, 0, 1000'000'000);
+ t.start();
+ auto got = TSP();
+ t.stop();
+
+ hash_t hash = 0;
+ for (int x : got) hash += x;
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/articulationPoints.bcc.cpp b/test/graph/articulationPoints.bcc.cpp
new file mode 100644
index 0000000..15f5cf2
--- /dev/null
+++ b/test/graph/articulationPoints.bcc.cpp
@@ -0,0 +1,78 @@
+#include "../util.h"
+struct edge {
+ ll from, to, id;
+};
+#define Edge edge
+#include <graph/articulationPoints.cpp>
+#undef Edge
+#include <datastructures/unionFind.cpp>
+
+vector<vector<int>> naiveBCC(int m) {
+ init(m);
+
+ vector<int> seen(sz(adj), -1);
+ int run = 0;
+ for (int i = 0; i < sz(adj); i++) {
+ for (auto e : adj[i]) {
+ run++;
+ seen[i] = run;
+ vector<ll> todo = {e.to};
+ seen[e.to] = run;
+ while (!todo.empty()) {
+ int c = todo.back();
+ todo.pop_back();
+ for (auto ee : adj[c]) {
+ if (seen[ee.to] == run) continue;
+ seen[ee.to] = run;
+ todo.push_back(ee.to);
+ }
+ }
+ for (auto ee : adj[i]) {
+ if (seen[ee.to] == run) unionSets(ee.id, e.id);
+ }
+ }
+ }
+ vector<vector<int>> res(m);
+ for (int i = 0; i < m; i++) {
+ res[findSet(i)].push_back(i);
+ }
+ for (auto& v : res) sort(all(v));
+ res.erase(remove_if(all(res), [](const vector<int>& v){return sz(v) <= 1;}), res.end());
+ sort(all(res));
+ return res;
+}
+
+void stress_test_bcc() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1)));
+ Graph<NoData, 0, 1> g(n);
+ g.erdosRenyi(m);
+
+ adj.assign(n, {});
+ int nextId = 0;
+ g.forEdges([&](int a, int b){
+ adj[a].push_back({a, b, nextId});
+ adj[b].push_back({b, a, nextId});
+ nextId++;
+ });
+
+ auto expected = naiveBCC(nextId);
+ find();
+ vector<vector<int>> got(sz(bcc));
+ for (int i = 0; i < sz(bcc); i++) {
+ for (auto e : bcc[i]) got[i].push_back(e.id);
+ sort(all(got[i]));
+ }
+ sort(all(got));
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+int main() {
+ stress_test_bcc();
+}
diff --git a/test/graph/articulationPoints.bridges.cpp b/test/graph/articulationPoints.bridges.cpp
new file mode 100644
index 0000000..a1b89d2
--- /dev/null
+++ b/test/graph/articulationPoints.bridges.cpp
@@ -0,0 +1,64 @@
+#include "../util.h"
+struct edge {
+ ll from, to, id;
+};
+#define Edge edge
+#include <graph/articulationPoints.cpp>
+#undef Edge
+
+vector<bool> naiveBridges(const vector<pair<int, int>>& edges) {
+ vector<bool> res(sz(edges));
+
+ vector<int> seen(sz(adj), -1);
+ for (int i = 0; i < sz(edges); i++) {
+ auto [a, b] = edges[i];
+ vector<int> todo = {a};
+ seen[a] = i;
+ while (!todo.empty() && seen[b] != i) {
+ int c = todo.back();
+ todo.pop_back();
+ for (auto e : adj[c]) {
+ if (e.id == i) continue;
+ if (seen[e.to] == i) continue;
+ seen[e.to] = i;
+ todo.push_back(e.to);
+ }
+ }
+ res[i] = seen[b] != i;
+ }
+ return res;
+}
+
+void stress_test_bridges() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1)));
+ Graph<NoData, 0, 1> g(n);
+ g.erdosRenyi(m);
+
+ adj.assign(n, {});
+ vector<pair<int, int>> edges;
+ g.forEdges([&](int a, int b){
+ adj[a].push_back({a, b, sz(edges)});
+ adj[b].push_back({b, a, sz(edges)});
+ edges.emplace_back(a, b);
+ });
+
+ auto expected = naiveBridges(edges);
+ find();
+ vector<bool> got(sz(edges));
+ for (auto e : bridges) {
+ if (got[e.id]) cerr << "error: duclicate" << FAIL;
+ got[e.id] = true;
+ }
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+int main() {
+ stress_test_bridges();
+}
diff --git a/test/graph/articulationPoints.cpp b/test/graph/articulationPoints.cpp
new file mode 100644
index 0000000..2567a09
--- /dev/null
+++ b/test/graph/articulationPoints.cpp
@@ -0,0 +1,85 @@
+#include "../util.h"
+struct edge {
+ ll from, to, id;
+};
+#define Edge edge
+#include <graph/articulationPoints.cpp>
+#undef Edge
+
+vector<bool> naiveArt() {
+ vector<bool> res(sz(adj));
+
+ vector<int> seen(sz(adj), -1);
+ for (int i = 0; i < sz(adj); i++) {
+ if (adj[i].empty()) continue;
+ seen[i] = i;
+ vector<ll> todo = {adj[i][0].to};
+ seen[todo[0]] = i;
+ while (!todo.empty()) {
+ int c = todo.back();
+ todo.pop_back();
+ for (auto e : adj[c]) {
+ if (seen[e.to] == i) continue;
+ seen[e.to] = i;
+ todo.push_back(e.to);
+ }
+ }
+ for (auto e : adj[i]) {
+ if (seen[e.to] != i) res[i] = true;
+ }
+ }
+ return res;
+}
+
+void stress_test_art() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, min<int>(300, n*(n-1) / 2 + 1)));
+ Graph<NoData, 0, 1> g(n);
+ g.erdosRenyi(m);
+
+ adj.assign(n, {});
+ int nextId = 0;
+ g.forEdges([&](int a, int b){
+ adj[a].push_back({a, b, nextId});
+ adj[b].push_back({b, a, nextId});
+ nextId++;
+ });
+
+ auto expected = naiveArt();
+ find();
+ vector<bool> got = isArt;
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+constexpr int M = 2'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData, 0, 1> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ int nextId = 0;
+ g.forEdges([&](int a, int b){
+ adj[a].push_back({a, b, nextId});
+ adj[b].push_back({b, a, nextId});
+ nextId++;
+ });
+
+ t.start();
+ find();
+ t.stop();
+ hash_t hash = sz(bridges) + sz(bcc);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test_art();
+ performance_test();
+}
diff --git a/test/graph/bellmannFord.cpp b/test/graph/bellmannFord.cpp
new file mode 100644
index 0000000..92f1fef
--- /dev/null
+++ b/test/graph/bellmannFord.cpp
@@ -0,0 +1,70 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+struct edge {
+ int from, to;
+ ll cost;
+};
+#include <graph/bellmannFord.cpp>
+namespace floydWarshall {
+#include <graph/floydWarshall.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+ vector<ll> potential = Random::integers<ll>(n, 0, 1'000'000'000'000ll);
+
+ vector<edge> edges;
+ floydWarshall::dist.assign(n, vector<ll>(n, INF));
+ for (int i = 0; i < n; i++) floydWarshall::dist[i][i] = 0;
+
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ ll w = Random::integer<ll>(1, 100'000'000'000ll);
+ w = potential[b] + w - potential[a];
+ edges.push_back({a, b, w});
+ floydWarshall::dist[a][b] = min(floydWarshall::dist[a][b], w);
+ });
+
+ floydWarshall::floydWarshall();
+ for (int i = 0; i < n; i++) {
+ auto got = bellmannFord(n, edges, i);
+ auto expected = floydWarshall::dist[i];
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 5'000;
+constexpr int M = 20'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ vector<edge> edges;
+ g.forEdges([&](int a, int b){
+ ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ edges.push_back({a, b, w1});
+ edges.push_back({b, a, w2});
+ });
+
+ t.start();
+ auto got = bellmannFord(N, edges, 0);
+ t.stop();
+ hash_t hash = 0;
+ for (auto x : got) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/bitonicTSP.cpp b/test/graph/bitonicTSP.cpp
new file mode 100644
index 0000000..7c448a2
--- /dev/null
+++ b/test/graph/bitonicTSP.cpp
@@ -0,0 +1,49 @@
+#include "../util.h"
+namespace got {
+#include <graph/bitonicTSP.cpp>
+}
+namespace expected {
+#include <graph/bitonicTSPsimple.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+
+ vector<vector<double>> dist(n);
+ for (auto& v : dist) v = Random::reals<double>(n, 0, 1e18);
+
+ got::dist = dist;
+ expected::dist = dist;
+
+ auto got = got::bitonicTSP();
+ auto expected = got::bitonicTSP();
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+//this is an easy graph...
+constexpr int N = 5'000;
+void performance_test() {
+ timer t;
+ got::dist = vector<vector<double>>(N);
+ for (auto& v : got::dist) v = Random::reals<double>(N, 0, 1e18);
+
+
+ t.start();
+ auto got = got::bitonicTSP();
+ t.stop();
+ hash_t hash = 0;
+ for (auto x : got) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/bitonicTSPsimple.cpp b/test/graph/bitonicTSPsimple.cpp
new file mode 100644
index 0000000..c79a0ef
--- /dev/null
+++ b/test/graph/bitonicTSPsimple.cpp
@@ -0,0 +1,49 @@
+#include "../util.h"
+namespace got {
+#include <graph/bitonicTSPsimple.cpp>
+}
+namespace expected {
+#include <graph/bitonicTSP.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+
+ vector<vector<double>> dist(n);
+ for (auto& v : dist) v = Random::reals<double>(n, 0, 1e18);
+
+ got::dist = dist;
+ expected::dist = dist;
+
+ auto got = got::bitonicTSP();
+ auto expected = got::bitonicTSP();
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+//this is an easy graph...
+constexpr int N = 2'000;
+void performance_test() {
+ timer t;
+ got::dist = vector<vector<double>>(N);
+ for (auto& v : got::dist) v = Random::reals<double>(N, 0, 1e18);
+
+
+ t.start();
+ auto got = got::bitonicTSP();
+ t.stop();
+ hash_t hash = 0;
+ for (auto x : got) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/blossom.cpp b/test/graph/blossom.cpp
new file mode 100644
index 0000000..714b029
--- /dev/null
+++ b/test/graph/blossom.cpp
@@ -0,0 +1,76 @@
+#include "../util.h"
+namespace tutte {
+void gauss(int n, ll mod);
+#include <graph/matching.cpp>
+#include <math/shortModInv.cpp>
+#include <math/lgsFp.cpp>
+}
+#include <graph/blossom.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 5'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1));
+
+ GM blossom(n);
+ srand(Random::rng());
+ tutte::adj.assign(n, {});
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ tutte::adj[a].push_back(b);
+ tutte::adj[b].push_back(a);
+
+ blossom.adj[a].push_back(b);
+ blossom.adj[b].push_back(a);
+ });
+
+ ll got = blossom.match();
+ ll expected = tutte::max_matching();
+
+ vector<bool> seen(n);
+ ll got2 = 0;
+ for (int i = 0; i < n; i++) {
+ int j = blossom.pairs[i];
+ if (j >= n) continue;
+ if (blossom.pairs[j] != i) cerr << "error: inconsitent" << FAIL;
+ if (j == i) cerr << "error: invalid" << FAIL;
+ if (j < i) continue;
+ if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL;
+ seen[i] = seen[j] = true;
+ got2++;
+ }
+
+ if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL;
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+//this is an easy graph...
+constexpr int N = 100'000;
+constexpr int M = 500'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ GM blossom(N);
+ g.forEdges([&](int a, int b){
+ blossom.adj[a].push_back(b);
+ blossom.adj[b].push_back(a);
+ });
+
+ t.start();
+ hash_t hash = blossom.match();
+ t.stop();
+ if (t.time > 200) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/bronKerbosch.cpp b/test/graph/bronKerbosch.cpp
new file mode 100644
index 0000000..1ccd493
--- /dev/null
+++ b/test/graph/bronKerbosch.cpp
@@ -0,0 +1,73 @@
+#include "../util.h"
+#include <graph/bronKerbosch.cpp>
+
+vector<bits> naiveCliques;
+
+void naive(bits mask = {}, int l = 0) {
+ bool maximal = true;
+ for (ll i = 0; i < l; i++) {
+ if (mask[i]) continue;
+ if ((adj[i] & mask) == mask) maximal = false;
+ }
+ for (; l < sz(adj); l++) {
+ if ((adj[l] & mask) == mask) {
+ maximal = false;
+ mask[l] = 1;
+ naive(mask, l + 1);
+ mask[l] = 0;
+ }
+ }
+ if (maximal and mask.any()) naiveCliques.push_back(mask);
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(2, 15);
+ int m = Random::integer<int>(0, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ adj.assign(n, {});
+ g.forEdges([&](int a, int b){
+ addEdge(a, b);
+ });
+
+ bronKerbosch();
+ naiveCliques.clear();
+ naive();
+
+ sort(all(cliques), [](bits a, bits b){return a.to_ullong() < b.to_ullong();});
+ sort(all(naiveCliques), [](bits a, bits b){return a.to_ullong() < b.to_ullong();});
+
+ if (cliques != naiveCliques) cerr << "got: " << sz(cliques) << ", expected: " << sz(naiveCliques) << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 55;
+constexpr int M = N*(N-1) / 2 - 2*N;
+void performance_test() {
+ timer t;
+
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ g.forEdges([&](int a, int b){
+ addEdge(a, b);
+ });
+
+ t.start();
+ bronKerbosch();
+ t.stop();
+
+ hash_t hash = sz(cliques);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/centroid.cpp b/test/graph/centroid.cpp
new file mode 100644
index 0000000..41d9d0f
--- /dev/null
+++ b/test/graph/centroid.cpp
@@ -0,0 +1,77 @@
+#include "../util.h"
+vector<vector<int>> adj;
+#include <graph/centroid.cpp>
+
+int subtreeSize(int c, int p) {
+ int res = 1;
+ for (int x : adj[c]) {
+ if (x == p) continue;
+ res += subtreeSize(x, c);
+ }
+ return res;
+}
+
+vector<int> naive() {
+ vector<int> res;
+ for (int i = 0; i < sz(adj); i++) {
+ bool isCentroid = true;
+ for (int j : adj[i]) isCentroid &= 2*subtreeSize(j, i) <= sz(adj);
+ if (isCentroid) res.push_back(i);
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 50);
+ Graph<NoData> g(n);
+ g.tree();
+
+ adj.assign(n, {});
+ g.forEdges([&](int a, int b){
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ });
+
+ auto expected = naive();
+ sort(all(expected));
+
+ for (int i = 0; i < n; i++) {
+ auto [a, b] = find_centroid(i);
+ vector<int> got;
+ if (a >= 0) got.push_back(a);
+ if (b >= 0) got.push_back(b);
+ sort(all(got));
+
+ if (got != expected) cerr << "error" << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 2'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.tree();
+
+ adj.assign(N, {});
+ g.forEdges([&](int a, int b){
+ adj[a].push_back(b);
+ adj[b].push_back(a);
+ });
+
+ t.start();
+ auto [gotA, gotB] = find_centroid();
+ t.stop();
+ hash_t hash = gotA + gotB;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/cycleCounting.cpp b/test/graph/cycleCounting.cpp
new file mode 100644
index 0000000..8e53aec
--- /dev/null
+++ b/test/graph/cycleCounting.cpp
@@ -0,0 +1,79 @@
+#include "../util.h"
+#include <datastructures/unionFind.cpp>
+#include <graph/cycleCounting.cpp>
+
+int naive(const vector<pair<int, int>>& edges, int n) {
+ int res = 0;
+ for (int i = 1; i < (1ll << sz(edges)); i++) {
+ vector<int> deg(n);
+ init(n);
+ int cycles = 0;
+ for (int j = 0; j < sz(edges); j++) {
+ if (((i >> j) & 1) != 0) {
+ auto [a, b] = edges[j];
+ deg[a]++;
+ deg[b]++;
+ if (findSet(a) != findSet(b)) {
+ unionSets(a, b);
+ } else {
+ cycles++;
+ }
+ }
+ }
+ bool ok = cycles == 1;
+ for (auto d : deg) ok &= d == 0 || d == 2;
+ if (ok) res++;
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 50'000; tries++) {
+ int n = Random::integer<int>(1, 8);
+ int m = Random::integer<int>(0, min<int>(15, n*(n-1) / 2 + 1));
+
+ Graph<NoData, 0, 1, 1> g(n);
+ g.erdosRenyi(m);
+ vector<pair<int, int>> edges;
+ cycles cyc(n);
+ g.forEdges([&](int a, int b){
+ edges.emplace_back(a, b);
+ cyc.addEdge(a, b);
+ });
+
+ int expected = naive(edges, n);
+ int got = cyc.count();
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 100;
+constexpr int M = 20;
+void performance_test() {
+ timer t;
+
+ Graph<NoData> g(N);
+ g.tree();
+ g.erdosRenyi(M);
+ cycles cyc(N);
+ g.forEdges([&](int a, int b){
+ cyc.addEdge(a, b);
+ });
+
+ t.start();
+ hash_t hash = cyc.count();
+ cerr << sz(cyc.base) << endl;
+ t.stop();
+
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/dijkstra.cpp b/test/graph/dijkstra.cpp
new file mode 100644
index 0000000..c0cfb7e
--- /dev/null
+++ b/test/graph/dijkstra.cpp
@@ -0,0 +1,64 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+#include <graph/dijkstra.cpp>
+struct edge {
+ int from, to;
+ ll cost;
+};
+#include <graph/bellmannFord.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+ vector<vector<path>> adj(n);
+ vector<edge> edges;
+
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ ll w = Random::integer<ll>(1, 1'000'000'000'000ll);
+ adj[a].push_back({w, b});
+ edges.push_back({a, b, w});
+ });
+
+ for (int i = 0; i < n; i++) {
+ auto got = dijkstra(adj, i);
+ auto expected = bellmannFord(n, edges, i);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+constexpr int M = 3'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ vector<vector<path>> adj(N);
+ g.forEdges([&](int a, int b){
+ ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ adj[a].push_back({w1, b});
+ adj[b].push_back({w2, a});
+ });
+
+ t.start();
+ auto got = dijkstra(adj, 0);
+ t.stop();
+ hash_t hash = 0;
+ for (auto x : got) hash += x;
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/dinicScaling.cpp b/test/graph/dinicScaling.cpp
new file mode 100644
index 0000000..967d6b1
--- /dev/null
+++ b/test/graph/dinicScaling.cpp
@@ -0,0 +1,61 @@
+#include "../util.h"
+namespace dinic {
+#include <graph/dinicScaling.cpp>
+}
+
+namespace pushRelabel {
+#include <graph/pushRelabel.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+ dinic::adj.assign(n, {});
+ pushRelabel::adj.assign(n, {});
+
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([](int a, int b){
+ ll w = Random::integer<ll>(1, 1'000'000'000'000ll);
+ dinic::addEdge(a, b, w);
+ pushRelabel::addEdge(a, b, w);
+ });
+
+ ll got = dinic::maxFlow(0, n - 1);
+ ll expected = pushRelabel::maxFlow(0, n - 1);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 50000;
+constexpr int M = 200000;
+void performance_test() {
+ using namespace dinic;
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ g.forEdges([](int a, int b){
+ ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ addEdge(a, b, w1);
+ addEdge(b, a, w2);
+ });
+
+ t.start();
+ hash_t hash = maxFlow(0, N - 1);
+ t.stop();
+ if (t.time > 2000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/euler.cpp b/test/graph/euler.cpp
new file mode 100644
index 0000000..6666040
--- /dev/null
+++ b/test/graph/euler.cpp
@@ -0,0 +1,87 @@
+#include "../util.h"
+struct Euler {
+ Euler(int n) : idx(n), validIdx(n) {}
+#include <graph/euler.cpp>
+};
+
+Euler eulerGraph(int n, int m) {
+ Euler res(n);
+
+ Graph<NoData> g(n);
+ g.tree();
+ g.forEdges([&](int a, int b) {
+ res.addEdge(a, b);
+ });
+
+ for (int i = n-1; i < m; i++) {
+ int a = Random::integer<int>(0, n);
+ int b = Random::integer<int>(0, n);
+ res.addEdge(a, b);
+ }
+ int last = -1;
+ for (int i = 0; i < n; i++) {
+ if (sz(res.idx[i]) % 2 != 0) {
+ if (last >= 0) {
+ res.addEdge(last, i);
+ last = -1;
+ } else {
+ last = i;
+ }
+ }
+ }
+ if (last >= 0) cerr << "FAIL" << FAIL;
+
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(n-1, 200);
+
+ auto g = eulerGraph(n, m);
+
+ vector<vector<int>> expected(n);
+ for (int i = 0; i < n; i++) {
+ for (int j : g.idx[i]) {
+ expected[i].push_back(g.to[j]);
+ }
+ sort(all(expected[i]));
+ }
+
+ g.euler(0);
+ vector<vector<int>> got(n);
+ if (g.cycle.front() != g.cycle.back()) cerr << "error: not cyclic" << FAIL;
+ for (int i = 1; i < sz(g.cycle); i++) {
+ int a = g.cycle[i-1];
+ int b = g.cycle[i];
+ got[a].push_back(b);
+ got[b].push_back(a);
+ }
+ for (auto& v : got) sort(all(v));
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 100'000;
+constexpr int M = 1'000'000;
+void performance_test() {
+ timer t;
+ auto g = eulerGraph(N, M);
+ t.start();
+ g.euler(0);
+ t.stop();
+ hash_t hash = 0;
+ for (int x : g.cycle) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/floydWarshall.cpp b/test/graph/floydWarshall.cpp
new file mode 100644
index 0000000..a93a9ea
--- /dev/null
+++ b/test/graph/floydWarshall.cpp
@@ -0,0 +1,90 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+struct edge {
+ int from, to;
+ ll cost;
+};
+#include <graph/bellmannFord.cpp>
+namespace floydWarshall {
+#include <graph/floydWarshall.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+ vector<ll> potential = Random::integers<ll>(n, 0, 1'000'000'000'000ll);
+
+ vector<edge> edges;
+ floydWarshall::dist.assign(n, vector<ll>(n, INF));
+ for (int i = 0; i < n; i++) floydWarshall::dist[i][i] = 0;
+
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ ll w = Random::integer<ll>(1, 100'000'000'000ll);
+ w = potential[b] + w - potential[a];
+ edges.push_back({a, b, w});
+ floydWarshall::dist[a][b] = min(floydWarshall::dist[a][b], w);
+ });
+
+ vector<vector<ll>> orig = floydWarshall::dist;
+
+ floydWarshall::floydWarshall();
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 10; j++) {
+ int k = Random::integer<int>(0, n);
+ auto path = floydWarshall::getPath(i, k);
+ if (path.empty() != (floydWarshall::dist[i][k] == INF)) cerr << "error: reconstruction" << FAIL;
+ if (path.empty()) continue;
+ if (path.front() != i) cerr << "error: start" << FAIL;
+ if (path.back() != k) cerr << "error: end" << FAIL;
+ for (int l = 1; l < sz(path); l++) {
+ if (floydWarshall::dist[i][path[l-1]] +
+ orig[path[l-1]][path[l]] +
+ floydWarshall::dist[path[l]][k] !=
+ floydWarshall::dist[i][k]) cerr << "error: edge" << FAIL;
+ }
+ }
+ }
+
+ for (int i = 0; i < n; i++) {
+ auto got = floydWarshall::dist[i];
+ auto expected = bellmannFord(n, edges, i);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500;
+constexpr int M = 20'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ floydWarshall::dist.assign(N, vector<ll>(N, INF));
+ for (int i = 0; i < N; i++) floydWarshall::dist[i][i] = 0;
+ g.forEdges([&](int a, int b){
+ ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ floydWarshall::dist[a][b] = w1;
+ floydWarshall::dist[b][a] = w2;
+ });
+
+ t.start();
+ floydWarshall::floydWarshall();
+ t.stop();
+ hash_t hash = 0;
+ for (auto x : floydWarshall::dist[42]) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/havelHakimi.cpp b/test/graph/havelHakimi.cpp
new file mode 100644
index 0000000..71476ec
--- /dev/null
+++ b/test/graph/havelHakimi.cpp
@@ -0,0 +1,65 @@
+#include "../util.h"
+#include <graph/havelHakimi.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 200'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, n*(n-1) / 2 + 1);
+ Graph g(n);
+ g.erdosRenyi(m);
+
+ vector<int> expected(n);
+ for (int i = 0; i < n; i++) expected[i] = g.deg(i);
+
+ auto res = havelHakimi(expected);
+ if (sz(res) != n) cerr << "error: wrong number of nodes" << FAIL;
+ vector<vector<int>> rev(n);
+ vector<int> got(n);
+ for (int i = 0; i < n; i++) {
+ got[i] = sz(res[i]);
+ for (int j : res[i]) {
+ if (j < 0 || j >= n) cerr << "error: invalid edge" << FAIL;
+ rev[j].push_back(i);
+ }
+ }
+
+ for (int i = 0; i < n; i++) {
+ sort(all(res[i]));
+ sort(all(rev[i]));
+ if (res[i] != rev[i]) cerr << "error: graph is directed" << FAIL;
+ for (int j : res[i]) if (j == i) cerr << "error: graph has loop" << FAIL;
+ for (int j = 1; j < sz(res[i]); j++) {
+ if (res[i][j] == res[i][j-1]) cerr << "error: multiedge" << FAIL;
+ }
+ }
+
+ if (expected != got) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 200'000;
+constexpr int M = 1'000'000;
+void performance_test() {
+ timer t;
+ Graph g(N);
+ g.erdosRenyi(M);
+
+ vector<int> expected(N);
+ for (int i = 0; i < N; i++) expected[i] = g.deg(i);
+
+ t.start();
+ auto res = havelHakimi(expected);
+ t.stop();
+ hash_t hash = 0;
+ for (auto& v : res) hash += sz(v);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/hopcroftKarp.cpp b/test/graph/hopcroftKarp.cpp
new file mode 100644
index 0000000..05599dd
--- /dev/null
+++ b/test/graph/hopcroftKarp.cpp
@@ -0,0 +1,74 @@
+#include "../util.h"
+namespace kuhn {
+#include <graph/maxCarBiMatch.cpp>
+}
+namespace hk {
+#include <graph/hopcroftKarp.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 50'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1));
+
+ kuhn::adj.assign(2*n, {});
+ hk::adj.assign(2*n, {});
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ kuhn::adj[a].push_back(n+b);
+ kuhn::adj[b+n].push_back(a);
+
+ hk::adj[a].push_back(n+b);
+ hk::adj[b+n].push_back(a);
+ });
+
+ ll got = hk::hopcroft_karp(n);
+ ll expected = kuhn::kuhn(n);
+
+ vector<bool> seen(2*n);
+ ll got2 = 0;
+ for (int i = 0; i < n; i++) {
+ int j = hk::pairs[i];
+ if (j < 0) continue;
+ if (hk::pairs[j] != i) cerr << "error: inconsitent" << FAIL;
+ if (j == i) cerr << "error: invalid" << FAIL;
+ if (j < i) continue;
+ if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL;
+ seen[i] = seen[j] = true;
+ got2++;
+ }
+
+ if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL;
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+//this is an easy graph...
+constexpr int N = 100'000;
+constexpr int M = 500'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ hk::adj.assign(2*N, {});
+ g.forEdges([&](int a, int b){
+ hk::adj[a].push_back(N+b);
+ hk::adj[b+N].push_back(a);
+ });
+
+ t.start();
+ hash_t hash = hk::hopcroft_karp(N);
+ t.stop();
+ if (t.time > 300) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/kruskal.cpp b/test/graph/kruskal.cpp
new file mode 100644
index 0000000..f6245b9
--- /dev/null
+++ b/test/graph/kruskal.cpp
@@ -0,0 +1,91 @@
+#include "../util.h"
+#include <datastructures/unionFind.cpp>
+
+struct edge {
+ int from, to;
+ ll cost;
+ bool operator<(const edge& o) const {
+ return cost > o.cost;
+ }
+};
+ll kruskal(vector<edge>& edges, int n) {
+ init(n);
+ #define Edge edge
+ #include <graph/kruskal.cpp>
+ #undef Edge
+ return cost;
+}
+
+ll prim(vector<edge>& edges, int n) {
+ vector<vector<pair<ll, int>>> adj(n);
+ for (auto [a, b, d] : edges) {
+ adj[a].emplace_back(d, b);
+ adj[b].emplace_back(d, a);
+ }
+ priority_queue<pair<ll, int>> todo;
+ vector<bool> seen(n);
+ ll res = 0;
+ for (ll i = 0; i < n; i++) {
+ if (seen[i]) continue;
+ todo.push({0, i});
+ while (!todo.empty()) {
+ auto [d, c] = todo.top();
+ todo.pop();
+ if (seen[c]) continue;
+ seen[c] = true;
+ res += d;
+ for (auto e : adj[c]) {
+ todo.push(e);
+ }
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(0, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ vector<edge> edges;
+ g.forEdges([&](int a, int b){
+ ll w = Random::integer<ll>(-1'000'000'000ll, 1'000'000'000ll);
+ edges.push_back({a, b, w});
+ });
+
+ ll got = kruskal(edges, n);
+ ll expected = prim(edges, n);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+constexpr int M = 3'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ vector<edge> edges;
+ g.forEdges([&](int a, int b){
+ ll w = Random::integer<ll>(-1'000'000'000ll, 1'000'000'000ll);
+ edges.push_back({a, b, w});
+ });
+
+ t.start();
+ hash_t hash = kruskal(edges, N);
+ t.stop();
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/matching.cpp b/test/graph/matching.cpp
new file mode 100644
index 0000000..b8fbc6c
--- /dev/null
+++ b/test/graph/matching.cpp
@@ -0,0 +1,62 @@
+#include "../util.h"
+namespace tutte {
+void gauss(int n, ll mod);
+#include <graph/matching.cpp>
+#include <math/shortModInv.cpp>
+#include <math/lgsFp.cpp>
+}
+#include <graph/blossom.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 5'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1));
+
+ GM blossom(n);
+ srand(Random::rng());
+ tutte::adj.assign(n, {});
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ tutte::adj[a].push_back(b);
+ tutte::adj[b].push_back(a);
+
+ blossom.adj[a].push_back(b);
+ blossom.adj[b].push_back(a);
+ });
+
+ ll got = tutte::max_matching();
+ ll expected = blossom.match();
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 125;
+constexpr int M = 5'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ srand(Random::rng());
+ tutte::adj.assign(N, {});
+ g.forEdges([&](int a, int b){
+ tutte::adj[a].push_back(b);
+ tutte::adj[b].push_back(a);
+ });
+
+ t.start();
+ hash_t hash = tutte::max_matching();
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/maxCarBiMatch.cpp b/test/graph/maxCarBiMatch.cpp
new file mode 100644
index 0000000..6d7fad0
--- /dev/null
+++ b/test/graph/maxCarBiMatch.cpp
@@ -0,0 +1,74 @@
+#include "../util.h"
+namespace kuhn {
+#include <graph/maxCarBiMatch.cpp>
+}
+namespace hk {
+#include <graph/hopcroftKarp.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 50'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, n*(n-1) / 2 + 1));
+
+ kuhn::adj.assign(2*n, {});
+ hk::adj.assign(2*n, {});
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([&](int a, int b){
+ kuhn::adj[a].push_back(n+b);
+ kuhn::adj[b+n].push_back(a);
+
+ hk::adj[a].push_back(n+b);
+ hk::adj[b+n].push_back(a);
+ });
+
+ ll got = kuhn::kuhn(n);
+ ll expected = hk::hopcroft_karp(n);
+
+ vector<bool> seen(2*n);
+ ll got2 = 0;
+ for (int i = 0; i < n; i++) {
+ int j = kuhn::pairs[i];
+ if (j < 0) continue;
+ if (kuhn::pairs[j] != i) cerr << "error: inconsitent" << FAIL;
+ if (j == i) cerr << "error: invalid" << FAIL;
+ if (j < i) continue;
+ if (seen[i] || seen[j]) cerr << "error: invalid" << FAIL;
+ seen[i] = seen[j] = true;
+ got2++;
+ }
+
+ if (got != got2) cerr << "got: " << got << ", got2: " << got2 << FAIL;
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+//this is an easy graph...
+constexpr int N = 10'000;
+constexpr int M = 100'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ kuhn::adj.assign(2*N, {});
+ g.forEdges([&](int a, int b){
+ kuhn::adj[a].push_back(N+b);
+ kuhn::adj[b+N].push_back(a);
+ });
+
+ t.start();
+ hash_t hash = kuhn::kuhn(N);
+ t.stop();
+ if (t.time > 200) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/maxWeightBipartiteMatching.cpp b/test/graph/maxWeightBipartiteMatching.cpp
new file mode 100644
index 0000000..d245405
--- /dev/null
+++ b/test/graph/maxWeightBipartiteMatching.cpp
@@ -0,0 +1,59 @@
+#include "../util.h"
+#pragma GCC diagnostic ignored "-Wshadow"
+namespace matching {
+ constexpr int N_LEFT = 1000;
+ constexpr int N_RIGHT = 1000;
+ constexpr double INF = LD::INF;
+ #include <graph/maxWeightBipartiteMatching.cpp>
+}
+namespace mcmf {
+ #include <graph/minCostMaxFlow.cpp>
+}
+
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ auto [l, r] = Random::pair<int>(1, 30);
+ mcmf::MinCostFlow mcmf(l+r+2, 0, 1);
+
+ for (int i = 0; i < l; i++) mcmf.addEdge(0, 2 + i, 1, 0);
+ for (int i = 0; i < r; i++) mcmf.addEdge(2 + l + i, 1, 1, 0);
+ for (int i = 0; i < l; i++) {
+ for (int j = 0; j < r; j++) {
+ matching::costs[i][j] = Random::integer<int>(-100, 100);
+ mcmf.addEdge(2 + i, 2 + l + j, 1, -matching::costs[i][j]);
+ }
+ }
+
+ double got = matching::match(l, r);
+ mcmf.mincostflow();
+ ll expected = -mcmf.mincost;
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += l + r;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test() {
+ using namespace matching;
+ timer t;
+
+ for (int i = 0; i < N_LEFT; i++) {
+ for (int j = 0; j < N_RIGHT; j++) {
+ costs[i][j] = Random::integer<int>(-100, 100);
+ }
+ }
+
+ t.start();
+ hash_t hash = match(N_LEFT, N_RIGHT);
+ t.stop();
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/minCostMaxFlow.cpp b/test/graph/minCostMaxFlow.cpp
new file mode 100644
index 0000000..8c92aa7
--- /dev/null
+++ b/test/graph/minCostMaxFlow.cpp
@@ -0,0 +1,68 @@
+#include "../util.h"
+#pragma GCC diagnostic ignored "-Wshadow"
+namespace matching {
+ constexpr int N_LEFT = 1000;
+ constexpr int N_RIGHT = 1000;
+ constexpr double INF = LD::INF;
+ #include <graph/maxWeightBipartiteMatching.cpp>
+}
+namespace mcmf {
+ #include <graph/minCostMaxFlow.cpp>
+}
+
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ auto [l, r] = Random::pair<int>(1, 30);
+ mcmf::MinCostFlow mcmf(l+r+2, 0, 1);
+
+ for (int i = 0; i < l; i++) mcmf.addEdge(0, 2 + i, 1, 0);
+ for (int i = 0; i < r; i++) mcmf.addEdge(2 + l + i, 1, 1, 0);
+ for (int i = 0; i < l; i++) {
+ for (int j = 0; j < r; j++) {
+ matching::costs[i][j] = Random::integer<int>(-100, 100);
+ mcmf.addEdge(2 + i, 2 + l + j, 1, -matching::costs[i][j]);
+ }
+ }
+
+ mcmf.mincostflow();
+ ll got = -mcmf.mincost;
+ double expected = matching::match(l, r);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += l + r;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000;
+constexpr int M = 10'000;
+void performance_test() {
+ using namespace mcmf;
+ timer t;
+
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ MinCostFlow mcmf(N, 0, 1);
+ vector<ll> potential = Random::integers<ll>(N, 0, 1'000'000ll);
+ g.forEdges([&](int a, int b){
+ ll c = Random::integer<ll>(1, 1000'000);
+ ll cost = Random::integer<ll>(0, 1000'000);
+ mcmf.addEdge(a, b, c, potential[b] + cost - potential[a]);
+ mcmf.addEdge(b, a, c, potential[a] + cost - potential[b]);
+ });
+
+ t.start();
+ mcmf.mincostflow();
+ t.stop();
+
+ hash_t hash = mcmf.mincost;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/pushRelabel.cpp b/test/graph/pushRelabel.cpp
new file mode 100644
index 0000000..ac3b079
--- /dev/null
+++ b/test/graph/pushRelabel.cpp
@@ -0,0 +1,61 @@
+#include "../util.h"
+namespace dinic {
+#include <graph/dinicScaling.cpp>
+}
+
+namespace pushRelabel {
+#include <graph/pushRelabel.cpp>
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+ dinic::adj.assign(n, {});
+ pushRelabel::adj.assign(n, {});
+
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([](int a, int b){
+ ll w = Random::integer<ll>(1, 1'000'000'000'000ll);
+ dinic::addEdge(a, b, w);
+ pushRelabel::addEdge(a, b, w);
+ });
+
+ ll got = pushRelabel::maxFlow(0, n - 1);
+ ll expected = dinic::maxFlow(0, n - 1);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 50000;
+constexpr int M = 200000;
+void performance_test() {
+ using namespace pushRelabel;
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ g.forEdges([](int a, int b){
+ ll w1 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ ll w2 = Random::integer<ll>(1, 1'000'000'000'000ll);
+ addEdge(a, b, w1);
+ addEdge(b, a, w2);
+ });
+
+ t.start();
+ hash_t hash = maxFlow(0, N - 1);
+ t.stop();
+ if (t.time > 300) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/scc.cpp b/test/graph/scc.cpp
new file mode 100644
index 0000000..123050f
--- /dev/null
+++ b/test/graph/scc.cpp
@@ -0,0 +1,92 @@
+#include "../util.h"
+#include <graph/scc.cpp>
+#include <datastructures/unionFind.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+ int m = Random::integer<int>(0, max<int>(1, min<int>(100, n*(n-1) / 2 + 1)));
+ Graph<NoData, true> g(n);
+ g.erdosRenyi(m);
+
+ adj.assign(n, {});
+ g.forEdges([](int a, int b){
+ adj[a].push_back(b);
+ });
+ scc();
+
+ vector<bool> tmp(n);
+ for (int i = 0; i < sz(sccs); i++) {
+ for (int x : sccs[i]) {
+ if (tmp[x]) cerr << "error: duclicate" << FAIL;
+ if (idx[x] != i) cerr << "error: inconsistent" << FAIL;
+ tmp[x] = true;
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ if (!tmp[i]) cerr << "error: missing" << FAIL;
+ }
+
+ init(n);
+ vector<ll> seen(n);
+ int tmpCounter = 0;
+ auto reach = [&](int a, int b) {
+ tmpCounter++;
+ seen[a] = tmpCounter;
+ vector<int> todo = {a};
+ while (seen[b] != tmpCounter && !todo.empty()) {
+ a = todo.back();
+ todo.pop_back();
+ g.forOut(a, [&](int /**/, int x){
+ if (seen[x] != tmpCounter) {
+ seen[x] = tmpCounter;
+ todo.push_back(x);
+ }
+ });
+ }
+ return seen[b] == tmpCounter;
+ };
+ for (int a = 0; a < n; a++) {
+ for (int b = 0; b < a; b++) {
+ if (findSet(a) == findSet(b)) continue;
+ if (reach(a, b) && reach(b, a)) unionSets(a, b);
+ }
+ }
+
+ for (int a = 0; a < n; a++) {
+ for (int b = 0; b <= a; b++) {
+ bool got = idx[a] == idx[b];
+ bool expected = findSet(a) == findSet(b);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+constexpr int M = 2'000'000;
+void performance_test() {
+ timer t;
+ Graph<NoData, true> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ g.forEdges([](int a, int b){
+ adj[a].push_back(b);
+ });
+
+ t.start();
+ scc();
+ t.stop();
+ hash_t hash = 0;
+ for (int x : idx) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/stoerWagner.cpp b/test/graph/stoerWagner.cpp
new file mode 100644
index 0000000..2003f09
--- /dev/null
+++ b/test/graph/stoerWagner.cpp
@@ -0,0 +1,81 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+
+namespace stoerWagner {
+#include <graph/stoerWagner.cpp>
+ void addEdge(int u, int v, ll c) {
+ adj[u].push_back({u, v, c});
+ adj[v].push_back({v, u, c});
+ }
+}
+
+namespace pushRelabel {
+#include <graph/pushRelabel.cpp>
+ ll minCut() {
+ ll res = INF;
+ for (int i = 0; i < sz(adj); i++) {
+ for (int j = 0; j < i; j++) {
+ if (i == j) continue;
+ res = min(res, maxFlow(i, j));
+ for (auto& v : adj) {
+ for (auto& e : v) {
+ e.f = 0;
+ }
+ }
+ }
+ }
+ return res;
+ }
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 5'000; tries++) {
+ int n = Random::integer<int>(2, 30);
+ int m = Random::integer<int>(n-1, max<int>(n, min<int>(500, n*(n-1) / 2 + 1)));
+
+ stoerWagner::adj.assign(n, {});
+ pushRelabel::adj.assign(n, {});
+
+ Graph<NoData> g(n);
+ g.erdosRenyi(m);
+ g.forEdges([](int a, int b){
+ ll w = Random::integer<ll>(1, 1'000'000'000'000ll);
+ stoerWagner::addEdge(a, b, w);
+ pushRelabel::addEdge(a, b, w);
+ pushRelabel::addEdge(b, a, w);
+ });
+
+ ll got = stoerWagner::stoer_wagner();
+ ll expected = pushRelabel::minCut();
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 200;
+constexpr int M = 10000;
+void performance_test() {
+ using namespace stoerWagner;
+ timer t;
+ Graph<NoData> g(N);
+ g.erdosRenyi(M);
+ adj.assign(N, {});
+ g.forEdges([](int a, int b){
+ ll w = Random::integer<ll>(1, 1'000'000'000'000ll);
+ addEdge(a, b, w);
+ });
+
+ t.start();
+ hash_t hash = stoer_wagner();
+ t.stop();
+ if (t.time > 2000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/graph/treeIsomorphism.cpp b/test/graph/treeIsomorphism.cpp
new file mode 100644
index 0000000..97f4df4
--- /dev/null
+++ b/test/graph/treeIsomorphism.cpp
@@ -0,0 +1,126 @@
+#include "../util.h"
+struct tree {
+ tree(int n) : adj(n) {}
+ #include <graph/treeIsomorphism.cpp>
+ #include <graph/centroid.cpp>
+
+ pair<int, int> treeLabel() {
+ auto [a, b] = find_centroid(0);
+ if (a >= 0) a = treeLabel(a);
+ if (b >= 0) b = treeLabel(b);
+ if (a > b) swap(a, b);
+ return {a, b};
+ }
+};
+
+void stress_test_eq() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 50);
+ Graph<NoData> g(n);
+ g.tree();
+
+ tree t(n);
+
+ g.forEdges([&](int a, int b){
+ t.adj[a].push_back(b);
+ t.adj[b].push_back(a);
+ });
+ auto [gotA, gotB] = t.treeLabel();
+
+ g.permutate();
+ t.adj.assign(n, {});
+ g.forEdges([&](int a, int b){
+ t.adj[a].push_back(b);
+ t.adj[b].push_back(a);
+ });
+ auto [expectedA, expectedB] = t.treeLabel();
+
+ if (gotA != expectedA) cerr << "got: " << gotA << ", expected: " << expectedA << FAIL;
+ if (gotB != expectedB) cerr << "got: " << gotB << ", expected: " << expectedB << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void test_tiny() {
+ vector<int> expected = {1,1,1,1,2,3,6,11,23}; //#A000055
+ for (int i = 1; i < sz(expected); i++) {
+ set<pair<int, int>> got;
+ tree t(i);
+
+ int labeled = 1;
+ for (int j = 3; j < i; j++) labeled *= i;
+ for (int j = 0; j < 10 * labeled; j++) {
+ Graph<NoData> g(i);
+ g.tree();
+
+ t.adj.assign(i, {});
+ g.forEdges([&](int a, int b){
+ t.adj[a].push_back(b);
+ t.adj[b].push_back(a);
+ });
+
+ got.insert(t.treeLabel());
+ }
+ if (sz(got) != expected[i]) cerr << i << ", got: " << sz(got) << ", expected: " << expected[i] << FAIL;
+ }
+ cerr << "tested tiny: " << sz(expected) << endl;
+}
+
+void stress_test_neq() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(20, 50);
+ Graph<NoData> g(n);
+ g.tree();
+
+ tree t(n);
+
+ g.forEdges([&](int a, int b){
+ t.adj[a].push_back(b);
+ t.adj[b].push_back(a);
+ });
+ auto [gotA, gotB] = t.treeLabel();
+
+ g.clear().tree();
+ t.adj.assign(n, {});
+ g.forEdges([&](int a, int b){
+ t.adj[a].push_back(b);
+ t.adj[b].push_back(a);
+ });
+ auto [expectedA, expectedB] = t.treeLabel();
+
+ if (gotA == expectedA && gotA >= 0) cerr << "error: " << n << ", " << tries << FAIL;
+ if (gotB == expectedB) cerr << "error: " << n << ", " << tries << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ Graph<NoData> g(N);
+ g.tree();
+
+ tree tt(N);
+ g.forEdges([&](int a, int b){
+ tt.adj[a].push_back(b);
+ tt.adj[b].push_back(a);
+ });
+
+ t.start();
+ auto [gotA, gotB] = tt.treeLabel();
+ t.stop();
+ hash_t hash = gotA + gotB;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test_eq();
+ test_tiny();
+ stress_test_neq();
+ performance_test();
+}
diff --git a/test/math/berlekampMassey.cpp b/test/math/berlekampMassey.cpp
new file mode 100644
index 0000000..58fd143
--- /dev/null
+++ b/test/math/berlekampMassey.cpp
@@ -0,0 +1,68 @@
+#include "../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/berlekampMassey.cpp>
+
+struct RandomRecurence {
+ vector<ll> f, c, cache;
+ RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {}
+ RandomRecurence(const vector<ll>& f_, const vector<ll>& c_) : c(c_), cache(f_) {
+ if (cache.size() < c.size()) cerr << "wrong size" << FAIL;
+ cache.resize(c.size());
+ f = cache;
+ }
+
+ ll operator()(ll k){
+ while (sz(cache) <= k) {
+ ll cur = 0;
+ for (ll i = 0; i < sz(c); i++) {
+ cur += (c[i] * cache[sz(cache) - i - 1]) % mod;
+ }
+ cur %= mod;
+ cache.push_back(cur);
+ }
+ return cache[k];
+ }
+};
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 50'000; i++) {
+ int n = Random::integer<int>(1, 10);
+ RandomRecurence expected(n);
+
+ ll k = Random::integer<ll>(2*n, 100);
+ vector<ll> s(k);
+ for (ll j = 0; j < k; j++) s[j] = expected(j);
+
+ auto res = BerlekampMassey(s);
+ RandomRecurence got(s, res);
+
+ for (ll j = 0; j < 3*k; j++) {
+ if (got(j) != expected(j)) cerr << "got: " << got(j) << ", expected: " << expected(j) << FAIL;
+ }
+
+ queries += k;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 5'000;
+void performance_test() {
+ timer t;
+ RandomRecurence f(N);
+ f(2*N);
+ t.start();
+ auto res = BerlekampMassey(f.cache);
+ t.stop();
+ hash_t hash = 0;
+ for (ll x : res) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/bigint.cpp b/test/math/bigint.cpp
new file mode 100644
index 0000000..3fc4ac1
--- /dev/null
+++ b/test/math/bigint.cpp
@@ -0,0 +1,122 @@
+#include "../util.h"
+#include <math/bigint.cpp>
+
+template<ll MOD>
+struct modInt {
+ ll value = 0;
+ modInt() {}
+ modInt(const bigint& x) {
+ stringstream a;
+ a << x;
+ string b = a.str();
+ for (ll i = b[0] == '-' ? 1 : 0; i < sz(b); i++) {
+ value *= 10;
+ value += b[i] - '0';
+ value %= MOD;
+ }
+ if (b[0] == '-') value = (MOD - value) % MOD;
+ }
+
+ modInt(ll x) : value(((x % MOD) + MOD) % MOD) {}
+
+ modInt operator+(modInt o) const {return value + o.value;}
+ modInt operator-(modInt o) const {return value - o.value;}
+ modInt operator*(modInt o) const {return value * o.value;}
+
+ modInt& operator+=(modInt o) {return *this = *this + o;}
+ modInt& operator-=(modInt o) {return *this = *this - o;}
+ modInt& operator*=(modInt o) {return *this = *this * o;}
+
+ ll& operator*() {return value;}
+ bool operator==(const modInt& o) const {return value == o.value;}
+ bool operator!=(const modInt& o) const {return value != o.value;}
+};
+
+constexpr ll MOD = 1'394'633'899;
+constexpr ll POOL = 8;
+
+void stress_test() {
+ int queries = 0;
+ for (int tries = 0; tries < 1000; tries++) {
+ vector<modInt<MOD>> expectedPool(POOL);
+ vector<bigint> gotPool(POOL);
+ for (int i = 0; i < POOL; i++) {
+ ll x = Random::integer<ll>(-1'000'000'000'000'000'000ll, 1'000'000'000'000'000'000ll);
+ expectedPool[i] = x;
+ gotPool[i] = x;
+ if (expectedPool[i] != modInt<MOD>(gotPool[i])) cerr << "error: 0" << FAIL;
+ }
+ for (int i = 0; i < 200; i++) {
+ int a = Random::integer<int>(0, POOL);
+ int b = Random::integer<int>(0, POOL);
+ int o = Random::integer<int>(0, 3);
+
+ if (Random::integer<int>(0, 2) == 0) {//x=
+ auto tmpExpected = expectedPool[a];
+ auto tmpGot = gotPool[a];
+
+ if (o == 0) {
+ tmpExpected += expectedPool[b];
+ tmpGot += gotPool[b];
+ }
+ if (o == 1) {
+ tmpExpected -= expectedPool[b];
+ tmpGot -= gotPool[b];
+ }
+ if (o == 2) {
+ tmpExpected -= expectedPool[b];
+ tmpGot -= gotPool[b];
+ }
+
+ if (tmpExpected != modInt<MOD>(tmpGot)) {
+ cerr << gotPool[a];
+ if (o == 0) cerr << "+";
+ if (o == 1) cerr << "-";
+ if (o == 2) cerr << "*";
+ cerr << gotPool[b] << "=" << tmpGot << endl;
+ cerr << "error: 1" << FAIL;
+ }
+
+ expectedPool[b] = tmpExpected;
+ gotPool[b] = tmpGot;
+ } else {//x
+ int c = Random::integer<int>(0, POOL);
+
+ modInt<MOD> tmpExpected;
+ bigint tmpGot;
+
+ if (o == 0) {
+ tmpExpected = expectedPool[a] + expectedPool[b];
+ tmpGot = gotPool[a] + gotPool[b];
+ }
+ if (o == 1) {
+ tmpExpected = expectedPool[a] - expectedPool[b];
+ tmpGot = gotPool[a] - gotPool[b];
+ }
+ if (o == 2) {
+ tmpExpected = expectedPool[a] * expectedPool[b];
+ tmpGot = gotPool[a] * gotPool[b];
+ }
+
+ if (tmpExpected != modInt<MOD>(tmpGot)) {
+ cerr << gotPool[a];
+ if (o == 0) cerr << "+";
+ if (o == 1) cerr << "-";
+ if (o == 2) cerr << "*";
+ cerr << gotPool[b] << "=" << tmpGot << endl;
+ cerr << "error: 2" << FAIL;
+ }
+
+ expectedPool[c] = tmpExpected;
+ gotPool[c] = tmpGot;
+ }
+ queries++;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/binomial0.cpp b/test/math/binomial0.cpp
new file mode 100644
index 0000000..00c04d4
--- /dev/null
+++ b/test/math/binomial0.cpp
@@ -0,0 +1,31 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+#include <math/multInv.cpp>
+constexpr ll mod = 1'394'633'899;
+#include <math/binomial0.cpp>
+
+
+void stress_test() {
+ vector<ll> last = {1};
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ for (ll j = 0; j <= i; j++) {
+ ll got = calc_binom(i, j);
+ ll expected = last[j];
+ if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += sz(last);
+
+ last.push_back(1);
+ for (ll j = i; j > 0; j--) {
+ last[j] = (last[j] + last[j - 1]) % mod;
+ }
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+int main() {
+ precalc();
+ stress_test();
+}
+
diff --git a/test/math/binomial1.cpp b/test/math/binomial1.cpp
new file mode 100644
index 0000000..f6fe20b
--- /dev/null
+++ b/test/math/binomial1.cpp
@@ -0,0 +1,27 @@
+#include "../util.h"
+#include <math/binomial1.cpp>
+
+
+void stress_test() {
+ vector<ll> last = {1};
+ ll queries = 0;
+ for (ll i = 0; i <= 61; i++) {
+ for (ll j = 0; j <= i; j++) {
+ ll got = calc_binom(i, j);
+ ll expected = last[j];
+ if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += sz(last);
+
+ last.push_back(1);
+ for (ll j = i; j > 0; j--) {
+ last[j] = last[j] + last[j - 1];
+ }
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/binomial2.cpp b/test/math/binomial2.cpp
new file mode 100644
index 0000000..b55c8af
--- /dev/null
+++ b/test/math/binomial2.cpp
@@ -0,0 +1,29 @@
+#include "../util.h"
+#include <math/primeSieve.cpp>
+#include <math/binomial2.cpp>
+
+
+void stress_test() {
+ vector<ll> last = {1};
+ ll queries = 0;
+ for (ll i = 0; i <= 1000; i++) {
+ for (ll j = 0; j <= i; j++) {
+ ll got = calc_binom(i, j);
+ ll expected = last[j];
+ if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += sz(last);
+
+ last.push_back(1);
+ for (ll j = i; j > 0; j--) {
+ last[j] = (last[j] + last[j - 1]) % mod;
+ }
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+int main() {
+ primeSieve();
+ stress_test();
+}
+
diff --git a/test/math/binomial3.cpp b/test/math/binomial3.cpp
new file mode 100644
index 0000000..4a99689
--- /dev/null
+++ b/test/math/binomial3.cpp
@@ -0,0 +1,31 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+#include <math/multInv.cpp>
+#include <math/binomial3.cpp>
+
+
+constexpr ll mod = 503;
+
+void stress_test() {
+ vector<ll> last = {1};
+ ll queries = 0;
+ for (ll i = 0; i < mod; i++) {
+ for (ll j = 0; j <= i; j++) {
+ ll got = calc_binom(i, j, mod);
+ ll expected = last[j];
+ if (got != expected) cerr << "calc_binom(" << i << ", " << j << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += sz(last);
+
+ last.push_back(1);
+ for (ll j = i; j > 0; j--) {
+ last[j] = (last[j] + last[j - 1]) % mod;
+ }
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/chineseRemainder.cpp b/test/math/chineseRemainder.cpp
new file mode 100644
index 0000000..26e71de
--- /dev/null
+++ b/test/math/chineseRemainder.cpp
@@ -0,0 +1,47 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+#include <math/chineseRemainder.cpp>
+
+struct NAIVE {
+ vector<pair<ll, ll>> added;
+ void add(ll a, ll m) {
+ added.emplace_back(a, m);
+ }
+ ll sol() const {
+ ll n = 1;
+ for (auto [_, x] : added) n = lcm(n, x);
+ for (ll i = 0; i < n; i++) {
+ bool ok = true;
+ for (auto [a, m] : added) {
+ ok &= (i % m) == a;
+ }
+ if (ok) return i;
+ }
+ return -1;
+ }
+};
+
+void stress_test() {
+ ll queries = 0;
+ ll withSol = 0;
+ for (ll i = 0; i < 100'000; i++) {
+ CRT crt;
+ NAIVE naive;
+ for (ll j = 0; j < 3; j++) {
+ int m = Random::integer<int>(1, 50);
+ int a = Random::integer<int>(0, m);
+ crt.add(a, m);
+ naive.add(a, m);
+ }
+ if (crt.hasSol != (naive.sol() >= 0)) cerr << "error" << FAIL;
+ if (crt.hasSol && crt.sol != naive.sol()) cerr << "got: " << (ll)crt.sol << ", expected: " << naive.sol() << FAIL;
+ queries += crt.M;
+ withSol += crt.hasSol;
+ }
+ cerr << "tested queries: " << queries << "(" << withSol << ")" << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/cycleDetection.cpp b/test/math/cycleDetection.cpp
new file mode 100644
index 0000000..bf57aed
--- /dev/null
+++ b/test/math/cycleDetection.cpp
@@ -0,0 +1,47 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+#include <math/cycleDetection.cpp>
+
+pair<ll, ll> naive(ll x0, function<ll(ll)> f) {
+ map<ll, ll> seen;
+ ll d = 0;
+ while (seen.find(x0) == seen.end()) {
+ seen[x0] = d;
+ d++;
+ x0 = f(x0);
+ }
+ return {seen[x0], d - seen[x0]};
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 1000'000; i++) {
+ int m = Random::integer<int>(1, 100);
+ int c = Random::integer<int>(0, m);
+ auto f = [&](ll x){return (x*x + c) % m;};
+ int x0 = Random::integer<int>(0, m);
+ auto got = cycleDetection(x0, f);
+ auto expected = naive(x0, f);
+ if (got != expected) cerr << "error: " << got.first << " " << got.second << " " << expected.first << " " << expected.second << FAIL;
+ queries += got.second;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr ll M = 18'086'183;
+void performance_test() {
+ timer t;
+ auto f = [&](ll x){return (1337*x + 42) % M;};
+ t.start();
+ auto [a, b] = cycleDetection(42, f);
+ t.stop();
+ hash_t hash = a + b;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/discreteLogarithm.cpp b/test/math/discreteLogarithm.cpp
new file mode 100644
index 0000000..0f9eecf
--- /dev/null
+++ b/test/math/discreteLogarithm.cpp
@@ -0,0 +1,64 @@
+#include "../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/legendre.cpp>
+
+ll overwrite = 0;
+ll getMemory(ll /**/) {return overwrite - 1;} //dlog code adds one...
+#define sqrtl getMemory
+#include <math/discreteLogarithm.cpp>
+#undef sqrtl
+
+template<typename F>
+void stress_test(F&& f) {
+ ll work = 0;
+ for (ll tries = 0; tries < 3'000; tries++) {
+ ll p = Random::prime<ll>(1'000);
+ overwrite = f(p);
+ ll a = Random::integer<ll>(1, p);
+ vector<bool> naive(p);
+ for (ll i = 0, j = 1; i < p; i++, j = (j * a) % p) {
+ naive[j] = true;
+ }
+ for (ll b = 0; b < p; b++) {
+ ll got = dlog(a, b, p);
+ if (got < -1 || got >= p) cerr << "error: out of range" << FAIL;
+ if ((got >= 0) != naive[b]) {
+ cerr << a << " " << b << " " << p << endl;
+ cerr << got << endl;
+ cerr << "error" << FAIL;
+ }
+ if (got >= 0 && powMod(a, got, p) != b) {
+ cerr << a << "^" << got << " = " << powMod(a, got, p) << " != " << b << " % " << p << endl;
+ cerr << "error: wrong" << FAIL;
+ }
+ work++;
+ }
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 25;
+constexpr ll mod = 1'394'633'899;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ overwrite = sqrt(mod);
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(1, mod);
+ ll b = Random::integer<ll>(0, mod);
+ t.start();
+ hash += dlog(a, b, mod);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test([](ll p){return sqrtl(p);});
+ stress_test([](ll p){return min<ll>(10, p - 1);});
+ stress_test([](ll p){return min<ll>(p - 1, sqrtl(p) + 100);});
+ performance_test();
+}
+
diff --git a/test/math/discreteNthRoot.cpp b/test/math/discreteNthRoot.cpp
new file mode 100644
index 0000000..d595e6d
--- /dev/null
+++ b/test/math/discreteNthRoot.cpp
@@ -0,0 +1,78 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/millerRabin.cpp>
+#include <math/rho.cpp>
+
+ll phi(ll pk, ll p, ll /*k*/) {return pk - pk / p;}
+ll phi(ll n) { // O(sqrt(n))
+ ll res = 1;
+ for (ll p = 2; p * p <= n; p++) {
+ if (n % p == 0) {
+ ll pk = 1;
+ ll k = 0;
+ do {
+ n /= p;
+ pk *= p;
+ k++;
+ } while (n % p == 0);
+ res *= phi(pk, p, k);
+ }}
+ if (n > 1) res *= phi(n, n, 1);
+ return res;
+}
+
+#include <math/primitiveRoot.cpp>
+#include <math/discreteLogarithm.cpp>
+#include <math/discreteNthRoot.cpp>
+
+//x^a=b mod m
+ll naiveRoot(ll a, ll b, ll m) {
+ for (ll i = 0; i < m; i++) {
+ if (powMod(i, a, m) == b) return i;
+ }
+ return -1;
+}
+
+void stress_test() {
+ int queries = 0;
+ int found = 0;
+ for (int tries = 0; tries < 50'000; tries++) {
+ ll p = Random::prime<ll>(0, 1000);
+ ll a = Random::integer<ll>(1, p);
+ ll b = Random::integer<ll>(1, p);
+
+ ll got = root(a, b, p);
+ ll expected = naiveRoot(a, b, p);
+
+ if (got < -1 || got >= p) cerr << "error: out of range" << FAIL;
+ if (got >= 0 && powMod(got, a, p) != b) cerr << "error: wrong" << FAIL;
+ if ((got >= 0) != (expected >= 0)) cerr << "error" << FAIL;
+ queries++;
+ if (expected >= 0) found++;
+ }
+ cerr << "tested random queries: " << queries << " (" << found << ")" << endl;
+}
+
+constexpr int N = 50;
+constexpr ll mod = 1'394'633'899;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int i = 0; i < N; i++) {
+ ll a = Random::integer<ll>(1, mod);
+ ll b = Random::integer<ll>(1, mod);
+ t.start();
+ hash += root(a, b, mod);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/divisors.cpp b/test/math/divisors.cpp
new file mode 100644
index 0000000..2402d2a
--- /dev/null
+++ b/test/math/divisors.cpp
@@ -0,0 +1,65 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/millerRabin.cpp>
+
+bool isSquare(ll x) {
+ ll r = sqrtl(x);
+ while (r*r > x) r--;
+ while ((r+1)*(r+1) <= x) r++;
+ return r*r==x;
+}
+
+#include <math/divisors.cpp>
+
+ll naive(ll x) {
+ ll res = 0;
+ for (ll i = 1; i*i <= x; i++) {
+ if (x % i == 0) {
+ res++;
+ if (i*i != x) res++;
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll work = 0;
+ for (ll i = 0; i < 1'000; i++) {
+ ll x = Random::integer<ll>(1, 1'000'000'000'000);
+ auto got = countDivisors(x);
+ auto expected = naive(x);
+ if (got != expected) cerr << "error: " << x << FAIL;
+ work += sqrt(x);
+ }
+ for (ll i = 0; i < 100'000; i++) {
+ ll x = Random::integer<ll>(1, 1'000'000);
+ auto got = countDivisors(x);
+ auto expected = naive(x);
+ if (got != expected) cerr << "error: " << x << FAIL;
+ work += sqrt(x);
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 200;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll x = Random::integer<ll>(1e18 / 2, 1e18);
+ t.start();
+ hash += countDivisors(x);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/extendedEuclid.cpp b/test/math/extendedEuclid.cpp
new file mode 100644
index 0000000..597f722
--- /dev/null
+++ b/test/math/extendedEuclid.cpp
@@ -0,0 +1,41 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+
+void stress_test() {
+ if (extendedEuclid(0, 0)[0] != 0) cerr << "error: extendedEuclid(0, 0)" << FAIL;
+ ll queries = 0;
+ timer t;
+ for (int i = 0; i < 1'000'000; i++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = 0;
+ {
+ t.start();
+ auto [got, x, y] = extendedEuclid(a, b);
+ t.stop();
+ ll expected = std::gcd(a, b);
+ if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ if (abs(x) >= max<ll>(2, abs(b))) cerr << "invalid x" << FAIL;
+ if (abs(y) >= max<ll>(2, abs(a))) cerr << "invalid y" << FAIL;
+ if (a*x + b*y != expected) cerr << "invalid x or y" << FAIL;
+ }
+ b = Random::integer<ll>(0, 1'000'000'000);
+ {
+ t.start();
+ auto [got, x, y] = extendedEuclid(a, b);
+ t.stop();
+ ll expected = std::gcd(a, b);
+ if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ if (abs(x) >= max<ll>(2, abs(b))) cerr << "invalid x" << FAIL;
+ if (abs(y) >= max<ll>(2, abs(a))) cerr << "invalid y" << FAIL;
+ if (a*x + b*y != expected) cerr << "invalid x or y" << FAIL;
+ }
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms" << endl;
+}
+
+int main() {
+ stress_test();
+}
diff --git a/test/math/gauss.cpp b/test/math/gauss.cpp
new file mode 100644
index 0000000..37bacce
--- /dev/null
+++ b/test/math/gauss.cpp
@@ -0,0 +1,118 @@
+#include "../util.h"
+constexpr double EPS = 1e-9;
+constexpr int UNIQUE = 1;
+constexpr int INCONSISTENT = 2;
+constexpr int MULTIPLE = 3;
+vector<vector<double>> mat;
+#include <math/gauss.cpp>
+
+vector<vector<double>> inverseMat(const vector<vector<double>>& m) {
+ int n = sz(m);
+ mat = m;
+ for (int i = 0; i < n; i++) {
+ if (sz(mat[i]) != n) cerr << "error: no square matrix" << FAIL;
+ mat[i].resize(2*n);
+ mat[i][n+i] = 1;
+ }
+ gauss(n);//the unique cetc. checks are not usefull since we dont solve an lgs...
+ vector<vector<double>> res(m);
+ for (int i = 0; i < n; i++) {
+ res[i] = vector<double>(mat[i].begin() + n, mat[i].end());
+ for (int j = 0; j < n; j++) {
+ if (j != i && mat[i][j] != 0) cerr << "error: not full rank?" << FAIL;
+ if (j == i && mat[i][j] == 0) cerr << "error: not full rank?" << FAIL;
+ }
+ }
+ return res;
+}
+
+vector<vector<double>> mul(const vector<vector<double>>& a, const vector<vector<double>>& b) {
+ int n = sz(a);
+ int m = sz(b[0]);
+ int x = sz(b);
+ if (sz(a[0]) != sz(b)) cerr << "error: wrong dimensions" << FAIL;
+ vector<vector<double>> res(n, vector<double>(m));
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ for (int k = 0; k < x; k++) {
+ res[i][j] += a[i][k] * b[k][j];
+ }
+ }
+ }
+ return res;
+}
+
+void test_tiny() {
+ mat = {
+ {1, 2, 3, 4},
+ {0, 5, 6, 7},
+ {0, 0, 8, 9},
+ };
+ if (gauss(sz(mat)) != UNIQUE) cerr << "error: 1" << FAIL;
+
+ mat = {
+ {-1, 1, 0, -1},
+ { 2, 6, 0, 10},
+ { 1, -2, 0, 0},
+ };
+ if (gauss(sz(mat)) != MULTIPLE) cerr << "error: 2" << FAIL;
+
+ mat = {
+ {-1, 1, 0, -1},
+ { 2, 6, 0, 10},
+ { 1, -2, 0, 1},
+ };
+ if (gauss(sz(mat)) != INCONSISTENT) cerr << "error: 3" << FAIL;
+}
+
+void stress_test_inv() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+
+ vector<vector<double>> m(n);
+ for (auto& v : m) v = Random::reals<double>(n, 0, 1'000);
+ // m hopefully has full rank...
+
+ auto inv = inverseMat(m);
+
+ auto prod = mul(m, inv);
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (i == j && abs(prod[i][j] - 1) >= EPS) cerr << "error: not inverted " << prod[i][j] << FAIL;
+ if (i != j && abs(prod[i][j] - 0) >= EPS) cerr << "error: not inverted " << prod[i][j] << FAIL;
+ }
+ }
+
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 250;
+void performance_test() {
+ timer t;
+
+ vector<vector<double>> m(N);
+ for (auto& v : m) v = Random::reals<double>(N, 0, 1'000);
+ mat = m;
+
+ t.start();
+ gauss(N);
+ t.stop();
+ hash_t hash = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ hash += mat[i][j];
+ }
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ test_tiny();
+ stress_test_inv();
+ performance_test();
+}
diff --git a/test/math/gcd-lcm.cpp b/test/math/gcd-lcm.cpp
new file mode 100644
index 0000000..294095b
--- /dev/null
+++ b/test/math/gcd-lcm.cpp
@@ -0,0 +1,46 @@
+#include "../util.h"
+#include <math/gcd-lcm.cpp>
+
+void stress_test() {
+ if (::gcd(0, 0) != 0) cerr << "error: gcd(0, 0)" << FAIL;
+ if (::lcm(0, 0) != 0) cerr << "error: lcm(0, 0)" << FAIL;
+ ll queries = 0;
+ timer t;
+ for (int i = 0; i < 1'000'000; i++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = 0;
+ {
+ ll got = ::gcd(a, b);
+ ll expected = std::gcd(a, b);
+ if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ {
+ ll got = ::lcm(a, b);
+ ll expected = std::lcm(a, b);
+ if (got != expected) cerr << "lcm(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ b = Random::integer<ll>(0, 1'000'000'000);
+ {
+ t.start();
+ ll got = ::gcd(a, b);
+ t.stop();
+ ll expected = std::gcd(a, b);
+ if (got != expected) cerr << "gcd(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ {
+ t.start();
+ ll got = ::lcm(a, b);
+ t.stop();
+ ll expected = std::lcm(a, b);
+ if (got != expected) cerr << "lcm(" << a << ", " << b << "), got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms" << endl;
+}
+
+int main() {
+ stress_test();
+}
diff --git a/test/math/goldenSectionSearch.cpp b/test/math/goldenSectionSearch.cpp
new file mode 100644
index 0000000..565a21c
--- /dev/null
+++ b/test/math/goldenSectionSearch.cpp
@@ -0,0 +1,74 @@
+#include "../util.h"
+#include <math/goldenSectionSearch.cpp>
+
+struct RandomFunction {
+ ld min;
+ vector<pair<ld, int>> polys;
+ RandomFunction(ld l, ld r) : min(Random::real<ld>(l, r)) {
+ do {
+ polys.emplace_back(
+ Random::real<ld>(0, 1e9),
+ 2 * Random::integer<int>(1, 5)
+ );
+ } while(false && Random::integer<int>(4) != 0);
+ }
+
+ ld operator()(ld x){
+ ld res = 0;
+ for (auto [m, p] : polys) {
+ res += m * pow(x - min, p);
+ }
+ return res;
+ }
+
+ friend ostream& operator<<(ostream& os, const RandomFunction& f) {
+ string plus = "";
+ for (auto [m, p] : f.polys) {
+ os << setprecision(15) << plus << m << "*(x-" << f.min << ")**" << p;
+ plus = "+";
+ }
+ return os;
+ }
+};
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 50'000; i++) {
+ ld l = Random::real<double>(-200, 200);
+ ld r = Random::real<double>(-200, 200);
+ if (l > r) swap(l, r);
+
+ RandomFunction f(l, r);
+
+ ld got = gss(l, r, f);
+ ld expected = f.min;
+ if (float_error(got, expected) > 1e-6) {
+ cerr << f << endl;
+ cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 10'000;
+void performance_test() {
+ timer t;
+ RandomFunction f(-200, 200);
+ f.polys.resize(1);
+
+ hash_t hash = 0;
+ for (int i = 0; i < N; i++) {
+ t.start();
+ hash += gss(-200, 200, f);
+ t.stop();
+ }
+ if (t.time > 1000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/inversions.cpp b/test/math/inversions.cpp
new file mode 100644
index 0000000..d2a54b7
--- /dev/null
+++ b/test/math/inversions.cpp
@@ -0,0 +1,43 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+#include <math/inversions.cpp>
+
+ll naive(const vector<ll>& v) {
+ ll res = 0;
+ for (ll i = 0; i < sz(v); i++) {
+ for (ll j = 0; j < i; j++) {
+ if (v[j] > v[i]) res++;
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 100'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto v = Random::integers<ll>(n, -50, 50);
+ ll got = inversions(v);
+ ll expected = naive(v);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 200'000;
+void performance_test() {
+ timer t;
+ auto v = Random::integers<ll>(N, -10'000, 10'000);
+ t.start();
+ hash_t hash = inversions(v);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/inversionsMerge.cpp b/test/math/inversionsMerge.cpp
new file mode 100644
index 0000000..85ab0d2
--- /dev/null
+++ b/test/math/inversionsMerge.cpp
@@ -0,0 +1,46 @@
+#include "../util.h"
+#include <math/inversionsMerge.cpp>
+
+ll naive(const vector<ll>& v) {
+ ll res = 0;
+ for (ll i = 0; i < sz(v); i++) {
+ for (ll j = 0; j < i; j++) {
+ if (v[j] > v[i]) res++;
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 100'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ vector<ll> v(n);
+ for (ll j = 0; j < n; j++) v[j] = (j-10) * 100000 + Random::integer<ll>(0, 10000);//values must be unique ):
+ shuffle(all(v), Random::rng);
+ ll expected = naive(v);
+ ll got = mergeSort(v);
+ if (got != expected) {
+ cerr << "got: " << got << ", expected: " << expected << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 2'000'000; //10 times faster
+void performance_test() {
+ timer t;
+ auto v = Random::integers<ll>(N, -10'000, 10'000);
+ t.start();
+ hash_t hash = mergeSort(v);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/kthperm.cpp b/test/math/kthperm.cpp
new file mode 100644
index 0000000..16691b9
--- /dev/null
+++ b/test/math/kthperm.cpp
@@ -0,0 +1,38 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+#include <math/kthperm.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ vector<ll> expected(n);
+ iota(all(expected), 0);
+ ll k = 0;
+ do {
+ auto got = kthperm(n, k);
+ if (got != expected) cerr << "error" << FAIL;
+ k++;
+ } while (k < 100 && next_permutation(all(expected)));
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ t.start();
+ auto got = kthperm(N, 4'168'751'907'498'170ll);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * got[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/kthperm_permIndex.cpp b/test/math/kthperm_permIndex.cpp
new file mode 100644
index 0000000..d84524e
--- /dev/null
+++ b/test/math/kthperm_permIndex.cpp
@@ -0,0 +1,21 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+#include <math/kthperm.cpp>
+#include <math/permIndex.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ ll n = Random::integer<ll>(20, 1000);
+ ll expected = Random::integer<ll>(0, 1'000'000'000'000'000'000);
+ ll got = permIndex(kthperm(n, expected));
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/legendre.cpp b/test/math/legendre.cpp
new file mode 100644
index 0000000..f210b57
--- /dev/null
+++ b/test/math/legendre.cpp
@@ -0,0 +1,43 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/legendre.cpp>
+
+void stress_test() {
+ ll work = 0;
+ for (ll i = 0; i < 5'000; i++) {
+ ll p = Random::prime<ll>(5'000);
+ vector<bool> isSquare(p);
+ for (ll j = 1; j < p; j++) isSquare[(j*j) % p] = true;
+ for (ll j = 0; j < p; j++) {
+ auto got = legendre(j, p);
+ auto expected = j == 0 ? 0 : (isSquare[j] ? 1 : -1);
+ if (got != expected) cerr << "error: " << j << " " << p << FAIL;
+ }
+ work += p;
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 1'000'000;
+constexpr ll mod = 1'394'633'899;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll j = Random::integer<ll>(mod);
+ t.start();
+ hash += legendre(j, mod);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/lgsFp.cpp b/test/math/lgsFp.cpp
new file mode 100644
index 0000000..08f8f84
--- /dev/null
+++ b/test/math/lgsFp.cpp
@@ -0,0 +1,118 @@
+#include "../util.h"
+#include <math/shortModInv.cpp>
+vector<vector<ll>> mat;
+#include <math/lgsFp.cpp>
+
+constexpr ll mod = 1'000'000'007;
+
+vector<vector<ll>> inverseMat(const vector<vector<ll>>& m) {
+ int n = sz(m);
+ mat = m;
+ for (int i = 0; i < n; i++) {
+ if (sz(mat[i]) != n) cerr << "error: no square matrix" << FAIL;
+ mat[i].resize(2*n);
+ mat[i][n+i] = 1;
+ }
+ gauss(n, mod);
+ vector<vector<ll>> res(m);
+ for (int i = 0; i < n; i++) {
+ res[i] = vector<ll>(mat[i].begin() + n, mat[i].end());
+ for (int j = 0; j < n; j++) {
+ if (j != i && mat[i][j] != 0) cerr << "error: not full rank?" << FAIL;
+ if (j == i && mat[i][j] != 1) cerr << "error: not full rank?" << FAIL;
+ }
+ }
+ return res;
+}
+
+vector<vector<ll>> mul(const vector<vector<ll>>& a, const vector<vector<ll>>& b) {
+ int n = sz(a);
+ int m = sz(b[0]);
+ int x = sz(b);
+ if (sz(a[0]) != sz(b)) cerr << "error: wrong dimensions" << FAIL;
+ vector<vector<ll>> res(n, vector<ll>(m));
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < m; j++) {
+ for (int k = 0; k < x; k++) {
+ res[i][j] += a[i][k] * b[k][j];
+ res[i][j] %= mod;
+ }
+ }
+ }
+ return res;
+}
+
+//this should just not crash...
+void test_square() {
+ ll queries = 0;
+ hash_t hash = 0;
+ for (int tries = 0; tries < 1'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+
+ vector<vector<ll>> m(n);
+ for (auto& v : m) v = Random::integers<ll>(n, 0, mod);
+ mat = m;
+ gauss(n, mod);
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ hash += mat[i][j];
+ }
+ }
+
+ queries += n;
+ }
+ cerr << "tested sqaures: " << queries << " (hash: " << hash << ")" << endl;;
+}
+
+void stress_test_inv() {
+ ll queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ int n = Random::integer<int>(1, 30);
+
+ vector<vector<ll>> m(n);
+ for (auto& v : m) v = Random::integers<ll>(n, 0, mod);
+ // m hopefully has full rank...
+
+ auto inv = inverseMat(m);
+
+ auto prod = mul(m, inv);
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ if (i == j && prod[i][j] != 1) cerr << "error: not inverted" << FAIL;
+ if (i != j && prod[i][j] != 0) cerr << "error: not inverted" << FAIL;
+ }
+ }
+
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 250;
+void performance_test() {
+ timer t;
+
+ vector<vector<ll>> m(N);
+ for (auto& v : m) v = Random::integers<ll>(N, 0, mod);
+ mat = m;
+
+ t.start();
+ gauss(N, mod);
+ t.stop();
+ hash_t hash = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ hash += mat[i][j];
+ }
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ test_square();
+ stress_test_inv();
+ performance_test();
+}
diff --git a/test/math/linearCongruence.cpp b/test/math/linearCongruence.cpp
new file mode 100644
index 0000000..ba8eeac
--- /dev/null
+++ b/test/math/linearCongruence.cpp
@@ -0,0 +1,53 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+#include <math/multInv.cpp>
+#include <math/linearCongruence.cpp>
+
+ll naive(ll a, ll b, ll m) {
+ for (ll x = 0; x < m; x++) {
+ if ((a * x) % m == b) return x;
+ }
+ return -1;
+}
+
+void stress_test() {
+ ll work = 0;
+ ll positive = 0;
+ for (ll tries = 0; tries < 500'000; tries++) {
+ ll m = Random::integer<ll>(0, 1'000);
+ ll a = Random::integer<ll>(0, m);
+ ll b = Random::integer<ll>(0, m);
+
+ ll got = solveLinearCongruence(a, b, m);
+ ll expected = naive(a, b, m);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << endl;
+ work++;
+ if (got >= 0) positive++;
+ }
+ cerr << "stress tested: " << work << " (" << positive << ")" << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll m = Random::integer<ll>(0, 1'0000'000'000);
+ ll a = Random::integer<ll>(0, m);
+ ll b = Random::integer<ll>(0, m);
+
+ t.start();
+ hash += solveLinearCongruence(a, b, m);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/linearRecurence.cpp b/test/math/linearRecurence.cpp
new file mode 100644
index 0000000..a5290e5
--- /dev/null
+++ b/test/math/linearRecurence.cpp
@@ -0,0 +1,54 @@
+#include "../util.h"
+#include <math/linearRecurence.cpp>
+
+struct RandomRecurence {
+ vector<ll> f, c, cache;
+ RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {}
+
+ ll operator()(ll k){
+ while (sz(cache) <= k) {
+ ll cur = 0;
+ for (ll i = 0; i < sz(c); i++) {
+ cur += (c[i] * cache[sz(cache) - i - 1]) % mod;
+ }
+ cur %= mod;
+ cache.push_back(cur);
+ }
+ return cache[k];
+ }
+};
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 10);
+ RandomRecurence f(n);
+ for (int j = 0; j < 100; j++) {
+ ll k = Random::integer<ll>(0, 1000);
+
+ ll got = kthTerm(f.f, f.c, k);
+ ll expected = f(k);
+
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000;
+void performance_test() {
+ timer t;
+ RandomRecurence f(N);
+ t.start();
+ hash_t hash = kthTerm(f.f, f.c, 1e18);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/linearSieve.cpp b/test/math/linearSieve.cpp
new file mode 100644
index 0000000..8ea822b
--- /dev/null
+++ b/test/math/linearSieve.cpp
@@ -0,0 +1,71 @@
+#include "../util.h"
+namespace expected {
+#include <math/primeSieve.cpp>
+}
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <math/linearSieve.cpp>
+
+void stress_test() {
+ expected::primeSieve();
+ expected::primes.resize(primes.size());
+ if (expected::primes != primes) cerr << "error: primes" << FAIL;
+ int queries = 0;
+ for (int i = 1; i < 1'000'000; i++) {
+ auto got = sieved[i];
+ auto expected = naive(i);
+ if (got != expected) cerr << i << ", got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ for (int i = 0; i < 1'000'000; i++) {
+ ll x = Random::integer<ll>(2, N);
+ auto got = sieved[x];
+ auto expected = naive(x);
+ if (got != expected) cerr << x << ", got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+void test_tiny() {
+ if (mu( 3, 3, 1) != -1) cerr << "error: 1" << FAIL;
+ if (mu( 9, 3, 2) != 0) cerr << "error: 2" << FAIL;
+ if (mu(27, 3, 3) != 0) cerr << "error: 3" << FAIL;
+
+ if (phi( 3, 3, 1) != 2) cerr << "error: 4" << FAIL;
+ if (phi( 9, 3, 2) != 6) cerr << "error: 5" << FAIL;
+ if (phi(27, 3, 3) != 18) cerr << "error: 6" << FAIL;
+
+ if (div( 3, 3, 1) != 2) cerr << "error: 7" << FAIL;
+ if (div( 9, 3, 2) != 3) cerr << "error: 8" << FAIL;
+ if (div(27, 3, 3) != 4) cerr << "error: 9" << FAIL;
+
+ if (divSum( 3, 3, 1) != 4) cerr << "error: 10" << FAIL;
+ if (divSum( 9, 3, 2) != 13) cerr << "error: 11" << FAIL;
+ if (divSum(27, 3, 3) != 40) cerr << "error: 12" << FAIL;
+
+ if (square( 3, 3, 1) != 1) cerr << "error: 13" << FAIL;
+ if (square( 9, 3, 2) != 9) cerr << "error: 14" << FAIL;
+ if (square(27, 3, 3) != 9) cerr << "error: 15" << FAIL;
+
+ if (squareFree( 3, 3, 1) != 3) cerr << "error: 13" << FAIL;
+ if (squareFree( 9, 3, 2) != 3) cerr << "error: 14" << FAIL;
+ if (squareFree(27, 3, 3) != 3) cerr << "error: 15" << FAIL;
+ cerr << "tested tiny" << endl;
+}
+
+void performance_test() {
+ timer t;
+ t.start();
+ sieve();
+ hash_t hash = sz(primes);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ performance_test();
+ stress_test();
+ test_tiny();
+}
+
diff --git a/test/math/longestIncreasingSubsequence.cpp b/test/math/longestIncreasingSubsequence.cpp
new file mode 100644
index 0000000..407dafe
--- /dev/null
+++ b/test/math/longestIncreasingSubsequence.cpp
@@ -0,0 +1,76 @@
+#include "../util.h"
+constexpr ll INF = LL::INF;
+#include <math/longestIncreasingSubsequence.cpp>
+#define lis unstrictLis
+#define lower_bound upper_bound
+#include <math/longestIncreasingSubsequence.cpp>
+#undef lower_bound
+#undef lis
+
+template<bool STRICT>
+bool isLis(const vector<ll>& a, const vector<int>& lis) {
+ for (int i = 1; i < sz(lis); i++) {
+ if (lis[i-1] >= lis[i]) return false;
+ if (a[lis[i-1]] > a[lis[i]]) return false;
+ if (STRICT && a[lis[i-1]] == a[lis[i]]) return false;
+ }
+ return true;
+}
+
+template<bool STRICT>
+vector<int> naive(const vector<ll>& a) {
+ vector<int> res;
+ for (ll i = 1; i < (1ll << sz(a)); i++) {
+ vector<int> tmp;
+ for (ll j = 0; j < sz(a); j++) {
+ if (((i >> j) & 1) != 0) tmp.push_back(j);
+ }
+ if (sz(tmp) >= sz(res) && isLis<STRICT>(a, tmp)) res = tmp;
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 12);
+ auto a = Random::integers<ll>(n, -10, 10);
+ auto expected = naive<true>(a);
+ auto got = lis(a);
+ if (got != expected) cerr << "error: strict" << FAIL;
+ queries += n;
+ }
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 12);
+ auto a = Random::integers<ll>(n, -10, 10);
+ auto expected = naive<false>(a);
+ auto got = unstrictLis(a);
+ if (got != expected) cerr << "error: not strict" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ auto a = Random::integers<ll>(N, -10'000, 10'000);
+ auto b = Random::integers<ll>(N, -10'000, 10'000);
+ sort(all(b));
+ auto c = Random::integers<ll>(N, -10'000, 10'000);
+ sort(all(c));
+ reverse(all(c));
+ hash_t hash = 0;
+ t.start();
+ hash += lis(a).size();
+ hash += lis(b).size();
+ hash += lis(c).size();
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/math/matrixPower.cpp b/test/math/matrixPower.cpp
new file mode 100644
index 0000000..4dfb0a8
--- /dev/null
+++ b/test/math/matrixPower.cpp
@@ -0,0 +1,116 @@
+#include "../util.h"
+
+constexpr ll mod = 1'394'633'899;
+
+struct mat {
+ vector<vector<ll>> m;
+ mat(int dim = 0, int diag = 1) : m(dim, vector<ll>(dim)) {
+ for (int i = 0; i < dim; i++) m[i][i] = diag;
+ }
+ mat(const vector<ll> c) : m(sz(c), vector<ll>(sz(c))) {
+ m[0] = c;
+ for (ll i = 1; i < sz(c); i++) {
+ m[i][i-1] = 1;
+ }
+ }
+
+ mat operator*(const mat& o) const {
+ int dim = sz(m);
+ mat res(dim, 0);
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < dim; j++) {
+ for (int k = 0; k < dim; k++) {
+ res.m[i][j] += m[i][k] * o.m[k][j];
+ res.m[i][j] %= mod;
+ }
+ }
+ }
+ return res;
+ }
+
+ vector<ll> operator*(const vector<ll>& o) const {
+ int dim = sz(m);
+ vector<ll> res(dim);
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < dim; j++) {
+ res[i] += m[i][j] * o[j];
+ res[i] %= mod;
+ }
+ }
+ return res;
+ }
+};
+
+#include <math/matrixPower.cpp>
+
+struct RandomRecurence {
+ vector<ll> f, c, cache;
+ RandomRecurence(int n) : f(Random::integers<ll>(n, 0, mod)), c(Random::integers<ll>(n, 0, mod)), cache(f) {}
+
+ ll operator()(ll k){
+ while (sz(cache) <= k) {
+ ll cur = 0;
+ for (ll i = 0; i < sz(c); i++) {
+ cur += (c[i] * cache[sz(cache) - i - 1]) % mod;
+ }
+ cur %= mod;
+ cache.push_back(cur);
+ }
+ return cache[k];
+ }
+};
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 10);
+ RandomRecurence f(n);
+ precalc(mat(f.c));
+ auto tmp = f.f;
+ reverse(all(tmp));
+
+ for (int j = 0; j < 100; j++) {
+ ll k = Random::integer<ll>(0, 1000);
+
+ vector<ll> got = calc(k, tmp);
+ vector<ll> expected(sz(f.f));
+ for (ll l = 0; l < n; l++) expected[n - 1 - l] = f(k + l);
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 100;
+constexpr int M = 500;
+void performance_test() {
+ timer t;
+ RandomRecurence f(N);
+ auto tmp = f.f;
+ reverse(all(tmp));
+
+ t.start();
+ precalc(mat(f.c));
+ t.stop();
+ if (t.time > 500) cerr << "too slow precalc: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms" << endl;
+
+ t.reset();
+ hash_t hash = 0;
+ for (int i = 0; i < M; i++) {
+ ll k = Random::integer<ll>(1e17,1e18);
+ t.start();
+ hash += calc(k, tmp).back();
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/millerRabin.base32.cpp b/test/math/millerRabin.base32.cpp
new file mode 100644
index 0000000..742d353
--- /dev/null
+++ b/test/math/millerRabin.base32.cpp
@@ -0,0 +1,137 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+
+//this is hacky...
+#define bool }\
+constexpr auto bases64 = c20::to_array(ignore::bases32);\
+bool
+namespace ignore {
+#include <math/millerRabin.cpp>
+#undef bool
+
+bool naive(ll x) {
+ for (ll i = 2; i*i <= x; i++) {
+ if (x % i == 0) return false;
+ }
+ return x > 1;
+}
+
+ll mul(const map<ll, int>& facts) {
+ ll res = 1;
+ for (auto [p, c] : facts) {
+ for (int i = 0; i < c; i++) res *= p;
+ }
+ if (abs(res) > (1ll << 62)) cerr << "invalid number: " << res << FAIL;
+ return res;
+}
+
+void extra_tests() {
+ vector<map<ll, int>> test = {
+ {{-1, 1}, {1, 1}},
+ {{-2, 1}, {1, 1}},
+ {{-7, 1}, {1, 1}},
+ {{-19812365821, 1}, {1, 1}},
+ {}, // 1
+ {{2, 1}},
+ {{3, 1}},
+ {{2, 2}},
+ {{5, 1}},
+ {{2, 1}, {3, 1}},
+ {{2, 2}, {3, 1}},
+ {{2, 1}, {3, 2}},
+ {{2, 2}, {3, 2}},
+ {{2, 62}},
+ {{2, 18}, {5, 18}},
+ {{352523, 1}, {352817, 1}},
+ {{41, 1}, {71, 1}, {421, 1}, {811, 1}},
+ {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}},
+ {{500000003, 1}, {1999999973, 1}},
+ {{65537, 2}},
+ {{999665081, 1}, {999716071, 1}},
+ {{550177, 1}, {1100353, 1}, {1650529, 1}},
+ {{459397, 1}, {918793, 1}, {1378189, 1}},
+ {{37, 1}, {109, 1}},
+ {{31, 1}, {151, 1}},
+ {{239, 1}, {1429, 1}},
+ {{89, 1}, {1093, 1}},
+ {{2, 3}, {15800133918749317, 1}},
+ {{12251, 1}, {85751, 1}},
+ {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}},
+ {{5, 1}, {1927962474784631, 1}},
+ {{197279, 1}, {1775503, 1}},
+ {{3716371, 1}, {14865481, 1}},
+ {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}},
+ {{4880401, 1}, {9760801, 1}},
+ {{2822159, 1}, {11288633, 1}},
+ {{3290341, 1}, {6580681, 1}},
+ {{611557, 1}, {1834669, 1}},
+ {{9227, 1}, {894923, 1}, {968731, 1}},
+ {{3, 4}, {13, 1}, {62633, 2}},
+ {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}},
+ {{332721269, 1}, {560937673, 1}},
+ {{30702523, 1}, {122810089, 1}},
+ {{24786439, 1}, {123932191, 1}},
+ {{382500329, 1}, {1530001313, 1}},
+ {{2, 4}, {5, 4}, {13, 1}, {30839, 2}},
+ {{3, 1}, {385417, 1}, {7985344259, 1}},
+ {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}},
+ {{627838711, 1}, {1212379867, 1}},
+ {{3, 5}, {5, 3}, {41, 2}, {157321, 2}},
+ {{5, 2}, {13, 1}},
+ {{3, 1}, {5, 5}},
+ {{2, 1}, {73, 1}, {193, 1}},
+ {{5, 2}, {13, 1}, {19, 1}, {73, 1}},
+ {{2, 3}, {3, 1}, {407521, 1}},
+ {{2, 1}, {3, 1}, {299210837, 1}},
+ {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}},
+ };
+
+ timer t;
+ for (auto factors : test) {
+ ll x = mul(factors);
+ if (x >= 1ll << 32) continue;
+ t.start();
+ auto got = isPrime(x);
+ t.stop();
+ bool expected = sz(factors) == 1 && factors.begin()->second == 1;
+ if (got != expected) cerr << "error: " << x << FAIL;
+ }
+ if (t.time > 10) cerr << "too slow" << FAIL;
+ cerr << "stress tested: " << t.time << "ms" << endl;
+}
+
+void stress_test() {
+ ll work = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ ll x = Random::integer<ll>(1, 1ll << 32);
+ auto got = isPrime(x);
+ auto expected = naive(x);
+ if (got != expected) cerr << "error: " << x << FAIL;
+ work += sqrt(x);
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 200'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll x = Random::integer<ll>(1ll << 31, 1ll << 32);
+ t.start();
+ hash += isPrime(x);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ extra_tests();
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/millerRabin.cpp b/test/math/millerRabin.cpp
new file mode 100644
index 0000000..fd98586
--- /dev/null
+++ b/test/math/millerRabin.cpp
@@ -0,0 +1,129 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/millerRabin.cpp>
+
+bool naive(ll x) {
+ for (ll i = 2; i*i <= x; i++) {
+ if (x % i == 0) return false;
+ }
+ return x > 1;
+}
+
+ll mul(const map<ll, int>& facts) {
+ ll res = 1;
+ for (auto [p, c] : facts) {
+ for (int i = 0; i < c; i++) res *= p;
+ }
+ if (abs(res) > (1ll << 62)) cerr << "invalid number: " << res << FAIL;
+ return res;
+}
+
+void extra_tests() {
+ vector<map<ll, int>> test = {
+ {{-1, 1}, {1, 1}},
+ {{-2, 1}, {1, 1}},
+ {{-7, 1}, {1, 1}},
+ {{-19812365821, 1}, {1, 1}},
+ {}, // 1
+ {{2, 1}},
+ {{3, 1}},
+ {{2, 2}},
+ {{5, 1}},
+ {{2, 1}, {3, 1}},
+ {{2, 2}, {3, 1}},
+ {{2, 1}, {3, 2}},
+ {{2, 2}, {3, 2}},
+ {{2, 62}},
+ {{2, 18}, {5, 18}},
+ {{352523, 1}, {352817, 1}},
+ {{41, 1}, {71, 1}, {421, 1}, {811, 1}},
+ {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}},
+ {{500000003, 1}, {1999999973, 1}},
+ {{65537, 2}},
+ {{999665081, 1}, {999716071, 1}},
+ {{550177, 1}, {1100353, 1}, {1650529, 1}},
+ {{459397, 1}, {918793, 1}, {1378189, 1}},
+ {{37, 1}, {109, 1}},
+ {{31, 1}, {151, 1}},
+ {{239, 1}, {1429, 1}},
+ {{89, 1}, {1093, 1}},
+ {{2, 3}, {15800133918749317, 1}},
+ {{12251, 1}, {85751, 1}},
+ {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}},
+ {{5, 1}, {1927962474784631, 1}},
+ {{197279, 1}, {1775503, 1}},
+ {{3716371, 1}, {14865481, 1}},
+ {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}},
+ {{4880401, 1}, {9760801, 1}},
+ {{2822159, 1}, {11288633, 1}},
+ {{3290341, 1}, {6580681, 1}},
+ {{611557, 1}, {1834669, 1}},
+ {{9227, 1}, {894923, 1}, {968731, 1}},
+ {{3, 4}, {13, 1}, {62633, 2}},
+ {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}},
+ {{332721269, 1}, {560937673, 1}},
+ {{30702523, 1}, {122810089, 1}},
+ {{24786439, 1}, {123932191, 1}},
+ {{382500329, 1}, {1530001313, 1}},
+ {{2, 4}, {5, 4}, {13, 1}, {30839, 2}},
+ {{3, 1}, {385417, 1}, {7985344259, 1}},
+ {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}},
+ {{627838711, 1}, {1212379867, 1}},
+ {{3, 5}, {5, 3}, {41, 2}, {157321, 2}},
+ {{5, 2}, {13, 1}},
+ {{3, 1}, {5, 5}},
+ {{2, 1}, {73, 1}, {193, 1}},
+ {{5, 2}, {13, 1}, {19, 1}, {73, 1}},
+ {{2, 3}, {3, 1}, {407521, 1}},
+ {{2, 1}, {3, 1}, {299210837, 1}},
+ {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}},
+ };
+
+ timer t;
+ for (auto factors : test) {
+ ll x = mul(factors);
+ t.start();
+ auto got = isPrime(x);
+ t.stop();
+ bool expected = sz(factors) == 1 && factors.begin()->second == 1;
+ if (got != expected) cerr << "error: " << x << FAIL;
+ }
+ if (t.time > 10) cerr << "too slow" << FAIL;
+ cerr << "stress tested: " << t.time << "ms" << endl;
+}
+
+void stress_test() {
+ ll work = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ ll x = Random::integer<ll>(1, 1'000'000'000'000);
+ auto got = isPrime(x);
+ auto expected = naive(x);
+ if (got != expected) cerr << "error: " << x << FAIL;
+ work += sqrt(x);
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 200'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll x = Random::integer<ll>(1e18 / 2, 1e18);
+ t.start();
+ hash += isPrime(x);
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ extra_tests();
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/modExp.cpp b/test/math/modExp.cpp
new file mode 100644
index 0000000..ebb38eb
--- /dev/null
+++ b/test/math/modExp.cpp
@@ -0,0 +1,42 @@
+#include "../util.h"
+#include <math/modExp.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int a = Random::integer<int>(1, 100);
+ int n = Random::integer<int>(2, 100);
+ ll expected = 1;
+ ll k = 0;
+ do {
+ auto got = powMod(a, k, n);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ k++;
+ expected = (expected * a) % n;
+ } while (k < 100);
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = Random::integer<ll>(0, 1'000'000'000);
+ ll n = Random::integer<ll>(2, 1'000'000'000);
+ t.start();
+ hash += powMod(a, b, n);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/modMulIterativ.cpp b/test/math/modMulIterativ.cpp
new file mode 100644
index 0000000..4f794c5
--- /dev/null
+++ b/test/math/modMulIterativ.cpp
@@ -0,0 +1,57 @@
+#include "../util.h"
+#include <math/modMulIterativ.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int a = Random::integer<int>(1, 100);
+ int n = Random::integer<int>(2, 100);
+ ll expected = 0;
+ ll k = 0;
+ do {
+ auto got = mulMod(a, k, n);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ k++;
+ expected = (expected + a) % n;
+ } while (k < 100);
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+void stress_test_large() {
+ ll queries = 0;
+ for (ll i = 0; i < 1000'000; i++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000'000'000'000);
+ ll b = Random::integer<ll>(0, 1'000'000'000'000'000'000);
+ ll n = Random::integer<ll>(2, 1'000'000'000'000'000'000);
+ ll expected = (lll)a * b % n;
+ auto got = mulMod(a, b, n);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000'000'000'000);
+ ll b = Random::integer<ll>(0, 1'000'000'000'000'000'000);
+ ll n = Random::integer<ll>(2, 1'000'000'000'000'000'000);
+ t.start();
+ hash += mulMod(a, b, n);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ stress_test_large();
+ performance_test();
+}
+
diff --git a/test/math/modPowIterativ.cpp b/test/math/modPowIterativ.cpp
new file mode 100644
index 0000000..2cf0eb4
--- /dev/null
+++ b/test/math/modPowIterativ.cpp
@@ -0,0 +1,42 @@
+#include "../util.h"
+#include <math/modPowIterativ.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int a = Random::integer<int>(1, 100);
+ int n = Random::integer<int>(2, 100);
+ ll expected = 1;
+ ll k = 0;
+ do {
+ auto got = powMod(a, k, n);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ k++;
+ expected = (expected * a) % n;
+ } while (k < 100);
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = Random::integer<ll>(0, 1'000'000'000);
+ ll n = Random::integer<ll>(2, 1'000'000'000);
+ t.start();
+ hash += powMod(a, b, n);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/multInv.cpp b/test/math/multInv.cpp
new file mode 100644
index 0000000..93763c5
--- /dev/null
+++ b/test/math/multInv.cpp
@@ -0,0 +1,40 @@
+#include "../util.h"
+#include <math/extendedEuclid.cpp>
+#include <math/multInv.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000'000; i++) {
+ ll n = Random::integer<ll>(2, 1'000'000'000);
+ ll x = 0;
+ do {
+ x = Random::integer<ll>(0, n);
+ } while (gcd(x, n) != 1);
+ ll y = multInv(x, n);
+ ll got = (x*y) % n;
+ if (got != 1) cerr << "got: " << got << ", expected: 1" << FAIL;
+ queries++;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = Random::integer<ll>(2, 1'000'000'000);
+ t.start();
+ hash += multInv(a, b);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/permIndex.cpp b/test/math/permIndex.cpp
new file mode 100644
index 0000000..61d34c8
--- /dev/null
+++ b/test/math/permIndex.cpp
@@ -0,0 +1,39 @@
+#include "../util.h"
+#include <datastructures/pbds.cpp>
+#include <math/permIndex.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ vector<ll> cur(n);
+ iota(all(cur), 0);
+ ll expected = 0;
+ do {
+ auto got = permIndex(cur);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ expected++;
+ } while (expected < 100 && next_permutation(all(cur)));
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ vector<ll> cur(N);
+ iota(all(cur), 0);
+ reverse(cur.end() - 10, cur.end());
+ t.start();
+ auto hash = permIndex(cur);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/piLegendre.cpp b/test/math/piLegendre.cpp
new file mode 100644
index 0000000..c3513bf
--- /dev/null
+++ b/test/math/piLegendre.cpp
@@ -0,0 +1,40 @@
+#include "../util.h"
+#include <math/primeSieve.cpp>
+namespace legendre {
+ #include <math/piLegendre.cpp>
+}
+namespace lehmer {
+ #include <math/piLehmer.cpp>
+}
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 1'000; i++) {
+ ll x = Random::integer<ll>(0, 1'000'000'000);
+ auto got = legendre::pi(x);
+ auto expected = lehmer::pi(x);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int i = 0; i < 1; i++) {
+ ll x = Random::integer<ll>(0, 1000'000'000'000);
+ t.start();
+ hash += legendre::pi(x);
+ t.stop();
+ }
+ if (t.time > 1500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ lehmer::init();
+ performance_test();
+ stress_test();
+}
+
diff --git a/test/math/piLehmer.cpp b/test/math/piLehmer.cpp
new file mode 100644
index 0000000..d84466f
--- /dev/null
+++ b/test/math/piLehmer.cpp
@@ -0,0 +1,42 @@
+#include "../util.h"
+#include <math/primeSieve.cpp>
+namespace legendre {
+ #include <math/piLegendre.cpp>
+}
+namespace lehmer {
+ #include <math/piLehmer.cpp>
+}
+
+void stress_test() {
+ int queries = 0;
+ for (int i = 0; i < 1'000; i++) {
+ ll x = Random::integer<ll>(0, 1'000'000'000);
+ auto got = lehmer::pi(x);
+ auto expected = legendre::pi(x);
+ if (got != expected) cerr << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ t.start();
+ lehmer::init();
+ t.stop();
+ for (int i = 0; i < 1; i++) {
+ ll x = Random::integer<ll>(0, 1000'000'000'000);
+ t.start();
+ hash += lehmer::pi(x);
+ t.stop();
+ }
+ if (t.time > 1500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ performance_test();
+ stress_test();
+}
+
diff --git a/test/math/primeSieve.cpp b/test/math/primeSieve.cpp
new file mode 100644
index 0000000..78a50d2
--- /dev/null
+++ b/test/math/primeSieve.cpp
@@ -0,0 +1,47 @@
+#include "../util.h"
+#include <math/primeSieve.cpp>
+
+bool naive(ll x) {
+ for (ll i = 2; i*i <= x; i++) {
+ if (x % i == 0) return false;
+ }
+ return x > 1;
+}
+
+void stress_test() {
+ int queries = 0;
+ vector<ll> found;
+ for (int i = -5; i < 1'000'000; i++) {
+ auto got = isPrime(i);
+ auto expected = naive(i);
+ if (got != expected) cerr << "error: " << i << FAIL;
+ if (got) found.push_back(i);
+ queries++;
+ }
+ primes.resize(sz(found));
+ if (primes != found) cerr << "error: primes" << FAIL;
+ for (int i = 0; i < 1'000'000; i++) {
+ ll x = Random::integer<ll>(2, N);
+ auto got = isPrime(x);
+ auto expected = naive(x);
+ if (got != expected) cerr << "error: " << x << FAIL;
+ queries++;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+void performance_test() {
+ timer t;
+ t.start();
+ primeSieve();
+ hash_t hash = sz(primes);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ performance_test();
+ stress_test();
+}
+
diff --git a/test/math/primitiveRoot.cpp b/test/math/primitiveRoot.cpp
new file mode 100644
index 0000000..cd0b388
--- /dev/null
+++ b/test/math/primitiveRoot.cpp
@@ -0,0 +1,82 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/millerRabin.cpp>
+#include <math/rho.cpp>
+
+ll phi(ll pk, ll p, ll /*k*/) {return pk - pk / p;}
+ll phi(ll n) { // O(sqrt(n))
+ ll res = 1;
+ for (ll p = 2; p * p <= n; p++) {
+ if (n % p == 0) {
+ ll pk = 1;
+ ll k = 0;
+ do {
+ n /= p;
+ pk *= p;
+ k++;
+ } while (n % p == 0);
+ res *= phi(pk, p, k);
+ }}
+ if (n > 1) res *= phi(n, n, 1);
+ return res;
+}
+
+#include <math/primitiveRoot.cpp>
+
+bool naiveIsPrimitive(ll g, ll n) {
+ if (gcd(g, n) != 1) return false;
+ vector<bool> seen(n);
+ ll c = g;
+ for (ll i = 0; i < n; i++) {
+ seen[c] = true;
+ c = (c * g) % n;
+ }
+ ll res = 0;
+ for (bool x : seen) if (x) res++;
+ return res == phi(n);
+}
+
+void stress_test() {
+ int queries = 0;
+ for (int tries = 0; tries < 20'000; tries++) {
+ ll a = Random::integer<ll>(1, 3);
+ ll p = Random::prime<ll>(0, 1000);
+ ll k = p == 2 ? 1 : Random::integer<ll>(1, log(100'000) / log(p) + 1);
+
+ ll x = a;
+ for (int i = 0; i < k; i++) x *= p;
+
+ ll got = findPrimitive(x);
+
+ if (got < 0 || got >= x) cerr << "error: out of range" << FAIL;
+ if (!naiveIsPrimitive(got, x)) cerr << "error: wrong" << got << " " << x << FAIL;
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void stress_test2() {
+ int queries = 0;
+ for (int x = 2; x < 5'000; x++) {
+ map<ll, int> facts;
+ factor(x, facts);
+ if (x % 2 == 0) facts.erase(facts.find(2));
+ bool expected = sz(facts) == 1;
+ if (x % 4 == 0) expected = false;
+ if (x == 2 || x == 4) expected = true;
+
+ bool got = findPrimitive(x) >= 0;
+
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+int main() {
+ stress_test();
+ stress_test2();
+}
+
diff --git a/test/math/rho.cpp b/test/math/rho.cpp
new file mode 100644
index 0000000..5e4792a
--- /dev/null
+++ b/test/math/rho.cpp
@@ -0,0 +1,117 @@
+#include "../util.h"
+#define ll lll
+#include <math/modPowIterativ.cpp>
+#undef ll
+#include <math/millerRabin.cpp>
+#include <math/rho.cpp>
+
+map<ll, int> factor(ll n) {
+ map<ll, int> facts;
+ factor(n, facts);
+ return facts;
+}
+
+ll mul(const map<ll, int>& facts) {
+ ll res = 1;
+ for (auto [p, c] : facts) {
+ for (int i = 0; i < c; i++) res *= p;
+ }
+ if (res < 1 || res > (1ll << 62)) cerr << "invalid number: " << res << FAIL;
+ return res;
+}
+
+void stress_test() {
+ vector<map<ll, int>> test = {
+ {}, // 1
+ {{2, 1}},
+ {{3, 1}},
+ {{2, 2}},
+ {{5, 1}},
+ {{2, 1}, {3, 1}},
+ {{2, 2}, {3, 1}},
+ {{2, 1}, {3, 2}},
+ {{2, 2}, {3, 2}},
+ {{2, 62}},
+ {{2, 18}, {5, 18}},
+ {{352523, 1}, {352817, 1}},
+ {{41, 1}, {71, 1}, {421, 1}, {811, 1}},
+ {{11, 1}, {17, 1}, {31, 1}, {61, 1}, {73, 1}, {66361, 1}},
+ {{500000003, 1}, {1999999973, 1}},
+ {{65537, 2}},
+ {{999665081, 1}, {999716071, 1}},
+ {{550177, 1}, {1100353, 1}, {1650529, 1}},
+ {{459397, 1}, {918793, 1}, {1378189, 1}},
+ {{37, 1}, {109, 1}},
+ {{31, 1}, {151, 1}},
+ {{239, 1}, {1429, 1}},
+ {{89, 1}, {1093, 1}},
+ {{2, 3}, {15800133918749317, 1}},
+ {{12251, 1}, {85751, 1}},
+ {{3, 1}, {5, 3}, {131, 1}, {6855593, 1}},
+ {{5, 1}, {1927962474784631, 1}},
+ {{197279, 1}, {1775503, 1}},
+ {{3716371, 1}, {14865481, 1}},
+ {{3, 1}, {5, 1}, {3075593, 1}, {3075593, 1}},
+ {{4880401, 1}, {9760801, 1}},
+ {{2822159, 1}, {11288633, 1}},
+ {{3290341, 1}, {6580681, 1}},
+ {{611557, 1}, {1834669, 1}},
+ {{9227, 1}, {894923, 1}, {968731, 1}},
+ {{3, 4}, {13, 1}, {62633, 2}},
+ {{2, 2}, {3, 1}, {5, 1}, {167, 2}, {299197, 2}},
+ {{332721269, 1}, {560937673, 1}},
+ {{30702523, 1}, {122810089, 1}},
+ {{24786439, 1}, {123932191, 1}},
+ {{382500329, 1}, {1530001313, 1}},
+ {{2, 4}, {5, 4}, {13, 1}, {30839, 2}},
+ {{3, 1}, {385417, 1}, {7985344259, 1}},
+ {{2, 4}, {3, 1}, {5, 1}, {7, 2}, {61, 1}, {179, 2}, {1381, 2}},
+ {{627838711, 1}, {1212379867, 1}},
+ {{3, 5}, {5, 3}, {41, 2}, {157321, 2}},
+ {{5, 2}, {13, 1}},
+ {{3, 1}, {5, 5}},
+ {{2, 1}, {73, 1}, {193, 1}},
+ {{5, 2}, {13, 1}, {19, 1}, {73, 1}},
+ {{2, 3}, {3, 1}, {407521, 1}},
+ {{2, 1}, {3, 1}, {299210837, 1}},
+ {{2, 8}, {3, 4}, {5, 2}, {7, 2}, {11, 1}, {13, 1}, {17, 1}, {19, 1}, {23, 1}, {29, 1}, {3137, 1}},
+ };
+
+ timer t;
+ for (auto expected : test) {
+ ll x = mul(expected);
+ t.start();
+ auto got = factor(x);
+ t.stop();
+ if (got != expected) {
+ cerr << "number: " << x << endl;
+ cerr << "got:" << endl;
+ for (auto [p, c] : got) cerr << p << "^" << c << endl;
+ cerr << "expected" << endl;
+ for (auto [p, c] : expected) cerr << p << "^" << c << endl;
+ cerr << FAIL;
+ }
+ }
+ if (t.time > 100) cerr << "too slow" << FAIL;
+ cerr << "stress tested: " << t.time << "ms" << endl;
+}
+
+constexpr int N = 2'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll x = Random::integer<ll>(1e18 / 2, 1e18);
+ t.start();
+ hash += factor(x).size();
+ t.stop();
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/shortModInv.cpp b/test/math/shortModInv.cpp
new file mode 100644
index 0000000..26960bf
--- /dev/null
+++ b/test/math/shortModInv.cpp
@@ -0,0 +1,39 @@
+#include "../util.h"
+#include <math/shortModInv.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000'000; i++) {
+ ll n = Random::integer<ll>(2, 1'000'000'000);
+ ll x = 0;
+ do {
+ x = Random::integer<ll>(0, n);
+ } while (gcd(x, n) != 1);
+ ll y = multInv(x, n);
+ ll got = (x*y) % n;
+ if (got != 1) cerr << "got: " << got << ", expected: 1" << FAIL;
+ queries++;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll a = Random::integer<ll>(0, 1'000'000'000);
+ ll b = Random::integer<ll>(2, 1'000'000'000);
+ t.start();
+ hash += multInv(a, b);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/simpson.cpp b/test/math/simpson.cpp
new file mode 100644
index 0000000..d7cdba3
--- /dev/null
+++ b/test/math/simpson.cpp
@@ -0,0 +1,63 @@
+#include "../util.h"
+std::function<double(double)> f;
+constexpr double EPS = 1e-9;
+#include <math/simpson.cpp>
+
+struct RandomPolynom {
+ vector<double> polynom;
+ RandomPolynom(int deg) : polynom(deg) {
+ for (auto& x : polynom) x = Random::real<double>(-100, 100);
+ }
+ double operator()(double x) const {
+ double res = 0;
+ double xx = 1;
+ for (double y : polynom ) {
+ res += xx * y;
+ xx *= x;
+ }
+ return res;
+ }
+ double area(double a, double b) const {
+ double res = 0;
+ double aa = a;
+ double bb = b;
+ ll d = 1;
+ for (double y : polynom) {
+ res += bb / d * y;
+ res -= aa / d * y;
+ aa *= a;
+ bb *= b;
+ d++;
+ }
+ return res;
+ }
+};
+
+void stress_test() {
+ timer t;
+ ll queries = 0;
+ for (int tries = 0; tries < 1'000; tries++) {
+ ll n = Random::integer<ll>(0, 6);
+ RandomPolynom poly(n);
+ f = poly;
+ for (ll i = 0; i < 200; i++) {
+ double l = Random::real<double>(-20, 20);
+ double r = Random::real<double>(-20, 20);
+ if (l > r) swap(l, r);
+
+ t.start();
+ double got = integrate(l, r);
+ t.stop();
+ double expected = poly.area(l, r);
+ if (float_error(got, expected) > 1e-6) cerr << fixed << setprecision(20) << "got: " << got << ", expected: " << expected << FAIL;
+ queries++;
+ }
+ }
+ if (t.time > 5000) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested random queries: " << queries << " (" << t.time << "ms)" << endl;
+}
+
+int main() {
+ stress_test();
+}
+
diff --git a/test/math/sqrtModCipolla.cpp b/test/math/sqrtModCipolla.cpp
new file mode 100644
index 0000000..26d975b
--- /dev/null
+++ b/test/math/sqrtModCipolla.cpp
@@ -0,0 +1,48 @@
+#include "../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/legendre.cpp>
+mt19937 rng(123456789);
+#include <math/sqrtModCipolla.cpp>
+
+void stress_test(ll range) {
+ ll work = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ ll p = Random::prime<ll>(range);
+ for (ll j = 0; j < 100; j++) {
+ ll x = Random::integer<ll>(0, p);
+ if (legendre(x, p) < 0) continue;
+
+ ll got = sqrtMod(x, p);
+ if (got < 0 || got >= p) cerr << "error: out of range" << FAIL;
+ if ((got * got) % p != x) cerr << "error: not root" << FAIL;
+ work++;
+ }
+ }
+ cerr << "stress tested: " << work << endl;
+}
+
+constexpr int N = 200'000;
+constexpr ll mod = 1'394'633'899;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ for (int operations = 0; operations < N; operations++) {
+ ll x;
+ do {
+ x = Random::integer<ll>(0, mod);
+ } while (legendre(x, mod) >= 0);
+ t.start();
+ hash += sqrtMod(x, mod);
+ t.stop();
+ }
+ if (t.time > 750) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+
+int main() {
+ stress_test(1'000);
+ stress_test(1'000'000'000);
+ performance_test();
+}
+
diff --git a/test/math/transforms/andTransform.cpp b/test/math/transforms/andTransform.cpp
new file mode 100644
index 0000000..fa029f6
--- /dev/null
+++ b/test/math/transforms/andTransform.cpp
@@ -0,0 +1,38 @@
+#include "../../util.h"
+#include <math/transforms/andTransform.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, -1000, 1000);
+ auto got = expected;
+ fft(got, false);
+ fft(got, true);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 23;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ fft(a, true);
+ fft(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * a[i];
+ for (ll i = 0; i < N; i++) hash += i * b[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/bitwiseTransforms.cpp b/test/math/transforms/bitwiseTransforms.cpp
new file mode 100644
index 0000000..132740c
--- /dev/null
+++ b/test/math/transforms/bitwiseTransforms.cpp
@@ -0,0 +1,38 @@
+#include "../../util.h"
+#include <math/transforms/bitwiseTransforms.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, -1000, 1000);
+ auto got = expected;
+ bitwiseConv(got, false);
+ bitwiseConv(got, true);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 23;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ bitwiseConv(a, true);
+ bitwiseConv(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * a[i];
+ for (ll i = 0; i < N; i++) hash += i * b[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/fft.cpp b/test/math/transforms/fft.cpp
new file mode 100644
index 0000000..858676b
--- /dev/null
+++ b/test/math/transforms/fft.cpp
@@ -0,0 +1,51 @@
+#include "../../util.h"
+#include <math/transforms/fft.cpp>
+
+vector<cplx> to_cplx(const vector<ll>& in) {
+ vector<cplx> res(sz(in));
+ for (int i = 0; i < sz(in); i++) res[i] = in[i];
+ return res;
+}
+
+vector<ll> from_cplx(const vector<cplx>& in) {
+ vector<ll> res(sz(in));
+ for (int i = 0; i < sz(in); i++) res[i] = llround(real(in[i]));
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, -1000, 1000);
+ vector<cplx> tmp = to_cplx(expected);
+ fft(tmp, false);
+ fft(tmp, true);
+ auto got = from_cplx(tmp);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 21;
+void performance_test() {
+ timer t;
+ auto a = to_cplx(Random::integers<ll>(N, -1000, 1000));
+ auto b = to_cplx(Random::integers<ll>(N, -1000, 1000));
+ t.start();
+ fft(a, true);
+ fft(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * llround(real(a[i]));
+ for (ll i = 0; i < N; i++) hash += i * llround(real(b[i]));
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/fftMul.cpp b/test/math/transforms/fftMul.cpp
new file mode 100644
index 0000000..5933864
--- /dev/null
+++ b/test/math/transforms/fftMul.cpp
@@ -0,0 +1,62 @@
+#include "../../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/transforms/fft.cpp>
+#pragma GCC diagnostic ignored "-Wnarrowing"
+#include <math/transforms/fftMul.cpp>
+
+vector<ll> from_cplx(const vector<cplx>& in) {
+ vector<ll> res(sz(in));
+ for (int i = 0; i < sz(in); i++) res[i] = llround(real(in[i]));
+ return res;
+}
+
+vector<ll> naive(const vector<ll>& a, const vector<ll>& b) {
+ vector<ll> res;
+ for (ll i = 1;; i *= 2) {
+ if (sz(a) + sz(b) <= i) {
+ res.resize(i, 0);
+ break;
+ }
+ }
+ for (int i = 0; i < sz(a); i++) {
+ for (int j = 0; j < sz(b); j++) {
+ res[i+j] += a[i] * b[j];
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ int m = Random::integer<int>(1, 100);
+ auto a = Random::integers<ll>(n, -1000, 1000);
+ auto b = Random::integers<ll>(m, -1000, 1000);
+ auto expected = naive(a, b);
+ auto got = from_cplx(mul(a, b));
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 20;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ auto got = from_cplx(mul(a, b));
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * got[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/multiplyBitwise.cpp b/test/math/transforms/multiplyBitwise.cpp
new file mode 100644
index 0000000..bc73290
--- /dev/null
+++ b/test/math/transforms/multiplyBitwise.cpp
@@ -0,0 +1,55 @@
+#include "../../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/transforms/bitwiseTransforms.cpp>
+#include <math/transforms/multiplyBitwise.cpp>
+
+vector<ll> naive(const vector<ll>& a, const vector<ll>& b) {
+ vector<ll> res;
+ for (ll i = 1;; i *= 2) {
+ if (sz(a) <= i && sz(b) <= i) {
+ res.resize(i, 0);
+ break;
+ }
+ }
+ for (int i = 0; i < sz(a); i++) {
+ for (int j = 0; j < sz(b); j++) {
+ res[i&j] += a[i] * b[j];
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ int m = Random::integer<int>(1, 100);
+ auto a = Random::integers<ll>(n, -1000, 1000);
+ auto b = Random::integers<ll>(m, -1000, 1000);
+ auto expected = naive(a, b);
+ auto got = mul(a, b);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 22;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ auto got = mul(a, b);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * got[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/multiplyFFT.cpp b/test/math/transforms/multiplyFFT.cpp
new file mode 100644
index 0000000..782be1b
--- /dev/null
+++ b/test/math/transforms/multiplyFFT.cpp
@@ -0,0 +1,55 @@
+#include "../../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/transforms/fft.cpp>
+#include <math/transforms/multiplyFFT.cpp>
+
+vector<ll> naive(const vector<ll>& a, const vector<ll>& b) {
+ vector<ll> res;
+ for (ll i = 1;; i *= 2) {
+ if (sz(a) + sz(b) <= i) {
+ res.resize(i, 0);
+ break;
+ }
+ }
+ for (int i = 0; i < sz(a); i++) {
+ for (int j = 0; j < sz(b); j++) {
+ res[i+j] += a[i] * b[j];
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ int m = Random::integer<int>(1, 100);
+ auto a = Random::integers<ll>(n, -1000, 1000);
+ auto b = Random::integers<ll>(m, -1000, 1000);
+ auto expected = naive(a, b);
+ auto got = mul(a, b);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 19;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ auto got = mul(a, b);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * got[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/multiplyNTT.cpp b/test/math/transforms/multiplyNTT.cpp
new file mode 100644
index 0000000..70fc137
--- /dev/null
+++ b/test/math/transforms/multiplyNTT.cpp
@@ -0,0 +1,56 @@
+#include "../../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/transforms/ntt.cpp>
+#include <math/transforms/multiplyNTT.cpp>
+
+vector<ll> naive(const vector<ll>& a, const vector<ll>& b) {
+ vector<ll> res;
+ for (ll i = 1;; i *= 2) {
+ if (sz(a) + sz(b) <= i) {
+ res.resize(i, 0);
+ break;
+ }
+ }
+ for (int i = 0; i < sz(a); i++) {
+ for (int j = 0; j < sz(b); j++) {
+ res[i+j] += a[i] * b[j];
+ res[i+j] %= mod;
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ int m = Random::integer<int>(1, 100);
+ auto a = Random::integers<ll>(n, 0, mod);
+ auto b = Random::integers<ll>(m, 0, mod);
+ auto expected = naive(a, b);
+ auto got = mul(a, b);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 20;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, 0, mod);
+ vector<ll> b = Random::integers<ll>(N, 0, mod);
+ t.start();
+ auto got = mul(a, b);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * got[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/ntt.cpp b/test/math/transforms/ntt.cpp
new file mode 100644
index 0000000..cd32073
--- /dev/null
+++ b/test/math/transforms/ntt.cpp
@@ -0,0 +1,39 @@
+#include "../../util.h"
+#include <math/modPowIterativ.cpp>
+#include <math/transforms/ntt.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, 0, mod);
+ auto got = expected;
+ ntt(got, false);
+ ntt(got, true);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 22;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, 0, mod);
+ vector<ll> b = Random::integers<ll>(N, 0, mod);
+ t.start();
+ ntt(a, true);
+ ntt(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * a[i];
+ for (ll i = 0; i < N; i++) hash += i * b[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/orTransform.cpp b/test/math/transforms/orTransform.cpp
new file mode 100644
index 0000000..0ec9155
--- /dev/null
+++ b/test/math/transforms/orTransform.cpp
@@ -0,0 +1,38 @@
+#include "../../util.h"
+#include <math/transforms/orTransform.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, -1000, 1000);
+ auto got = expected;
+ fft(got, false);
+ fft(got, true);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 23;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ fft(a, true);
+ fft(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * a[i];
+ for (ll i = 0; i < N; i++) hash += i * b[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/math/transforms/xorTransform.cpp b/test/math/transforms/xorTransform.cpp
new file mode 100644
index 0000000..17b0f6f
--- /dev/null
+++ b/test/math/transforms/xorTransform.cpp
@@ -0,0 +1,38 @@
+#include "../../util.h"
+#include <math/transforms/xorTransform.cpp>
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = 1ll << Random::integer<int>(0, 10);
+ auto expected = Random::integers<ll>(n, -1000, 1000);
+ auto got = expected;
+ fft(got, false);
+ fft(got, true);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested queries: " << queries << endl;
+}
+
+constexpr int N = 1ll << 23;
+void performance_test() {
+ timer t;
+ vector<ll> a = Random::integers<ll>(N, -1000, 1000);
+ vector<ll> b = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ fft(a, true);
+ fft(b, false);
+ t.stop();
+ hash_t hash = 0;
+ for (ll i = 0; i < N; i++) hash += i * a[i];
+ for (ll i = 0; i < N; i++) hash += i * b[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/compiletime.cpp b/test/other/compiletime.cpp
new file mode 100644
index 0000000..591669d
--- /dev/null
+++ b/test/other/compiletime.cpp
@@ -0,0 +1,2 @@
+#include <other/compiletime.cpp>
+int main() {} \ No newline at end of file
diff --git a/test/other/divideAndConquer.cpp b/test/other/divideAndConquer.cpp
new file mode 100644
index 0000000..a6fda9d
--- /dev/null
+++ b/test/other/divideAndConquer.cpp
@@ -0,0 +1,103 @@
+#include "../util.h"
+constexpr ll inf = LL::INF;
+#include <other/divideAndConquer.cpp>
+
+vector<vector<ll>> gen(int n) {
+ vector<vector<ll>> res(n, vector<ll>(n));
+ ll mi = 0;
+ for (ll a = n-1; a >= 0; a--) {
+ for (ll c = n-1; c >= a; c--) {
+ for (ll b = a; b <= c; b++) {
+ for (ll d = c; d < n; d++) {
+ res[a][c] = min(res[a][c], res[a][d] + res[b][c] - res[b][d]);
+ }
+ }
+ res[a][c] -= Random::integer<ll>(0, 1000);
+ mi = min(mi, res[a][c]);
+ }
+ }
+ for (auto& v : res) for (auto& x : v) x -= mi;
+
+ for (ll a = 0; a < n; a++) {
+ for (ll b = a; b < n; b++) {
+ for (ll c = b; c < n; c++) {
+ for (ll d = c; d < n; d++) {
+ if (res[a][d] < 0 || res[a][d] + res[b][c] < res[a][c] + res[b][d]) {
+ cerr << "invalid C array!" << FAIL;
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+vector<vector<ll>> genQuick(int n) {
+ vector<vector<ll>> res(n, vector<ll>(n));
+ for (ll a = n-1; a >= 0; a--) {
+ for (ll c = n-1; c >= a; c--) {
+ res[a][c] = (c-a) * (c - a) + Random::integer<ll>(0, 2);
+ }
+ }
+ return res;
+}
+
+/*ll naive(int n, int m) {
+ vector<vector<ll>> state(m+1, vector<ll>(n+1, inf));
+ state[0][0] = 0;
+ for (int i = 1; i <= m; i++) {
+ for (int j = 1; j <= n; j++) {
+ for (int k = 1; k <= j; k++) {
+ state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]);
+ }
+ }
+ }
+ return state[m][n];
+}*/
+
+vector<ll> naive(int n) {
+ vector<vector<ll>> state(n+1, vector<ll>(n+1, inf));
+ state[0][0] = 0;
+ vector<ll> res(n+1, inf);
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= n; j++) {
+ for (int k = 1; k <= j; k++) {
+ state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]);
+ }
+ }
+ res[i] = state[i][n];
+ }
+ return res;
+}
+
+void stress_test() {
+ ll tests = 0;
+ for (ll i = 0; i < 1000; i++) {
+ auto n = Random::integer(10, 20);
+ C = gen(n);
+ auto expected = naive(n);
+ for (ll m = 1; m <= n; m++) {
+ auto got = calc(n, m);
+ if (got != expected[m]) cerr << "got: " << got << ", expected: " << expected[m] << FAIL;
+ tests++;
+ }
+ }
+ cerr << "tested random queries: " << tests << endl;
+}
+
+constexpr int N = 10'000;
+void performance_test() {
+ timer t;
+ C = genQuick(N);
+ t.start();
+ auto hash = calc(N, 32);
+ t.stop();
+ if (t.time > 50) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/fastIO.cpp b/test/other/fastIO.cpp
new file mode 100644
index 0000000..765ddba
--- /dev/null
+++ b/test/other/fastIO.cpp
@@ -0,0 +1,32 @@
+#include "../util.h"
+#include <other/fastIO.cpp>
+
+int main() {
+ if (freopen("other/fastIO.in", "r", stdin) == nullptr) cerr << "fastIO.in not found" << FAIL;
+ vector<int> got(5);
+ vector<int> expected = {4, 7, 3, 6, 9};
+ for (int& x : got) fastscan(x);
+ if (got != expected) cerr << "failed fastscan" << FAIL;
+
+ if (freopen("other/fastIO.out", "w", stdout) == nullptr) cerr << "fastIO.out not writebale" << FAIL;
+ fastprint(0);
+ putchar('\n');
+ fastprint(-1);
+ putchar(' ');
+ fastprint(-8321648);
+ putchar(' ');
+ fastprint(1);
+ putchar(' ');
+ fastprint(42387);
+ putchar('\n');
+ fclose(stdout);
+
+ stringstream buffer;
+ {
+ ifstream tmp("other/fastIO.out");
+ buffer << tmp.rdbuf();
+ }
+ if (buffer.str() != "0\n-1 -8321648 1 42387\n") cerr << "failed fastprint" << FAIL;
+ cerr << "done" << endl;
+}
+
diff --git a/test/other/fastIO.in b/test/other/fastIO.in
new file mode 100644
index 0000000..45594a4
--- /dev/null
+++ b/test/other/fastIO.in
@@ -0,0 +1,2 @@
+4 7
+3 6 9 \ No newline at end of file
diff --git a/test/other/josephus2.cpp b/test/other/josephus2.cpp
new file mode 100644
index 0000000..d28fe0d
--- /dev/null
+++ b/test/other/josephus2.cpp
@@ -0,0 +1,42 @@
+#include "../util.h"
+#include <other/josephus2.cpp>
+
+template<ll O>
+ll naive(ll n, ll k) {
+ vector<ll> state(n);
+ iota(all(state), O);
+ for (ll i = k-1; state.size() > 1; i = (i + k - 1) % sz(state)) {
+ state.erase(state.begin() + i);
+ }
+ return state[0];
+}
+
+void stress_test() {
+ ll tests = 0;
+ for (ll i = 1; i < 2'000; i++) {
+ auto got = rotateLeft(i);
+ auto expected = naive<1>(i, 2);
+ if (got != expected) cerr << "error: " << i << FAIL;
+ tests++;
+ }
+ cerr << "tested queries: " << tests << endl;
+}
+
+constexpr int N = 1'000'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ t.start();
+ for (ll i = 0; i < N; i++) {
+ hash += rotateLeft(1'000'000'000'000'000'000ll + i);
+ }
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/josephusK.cpp b/test/other/josephusK.cpp
new file mode 100644
index 0000000..e837640
--- /dev/null
+++ b/test/other/josephusK.cpp
@@ -0,0 +1,43 @@
+#include "../util.h"
+#include <other/josephus2.cpp>
+#include <other/josephusK.cpp>
+
+template<ll O>
+ll naive(ll n, ll k) {
+ vector<ll> state(n);
+ iota(all(state), O);
+ for (ll i = k-1; state.size() > 1; i = (i + k - 1) % sz(state)) {
+ state.erase(state.begin() + i);
+ }
+ return state[0];
+}
+
+void stress_test() {
+ ll tests = 0;
+ for (ll i = 1; i < 500; i++) {
+ for (ll j = 1; j <= i; j++) {
+ auto got = josephus(i, j);
+ auto expected = naive<0>(i, j);
+ if (got != expected) cerr << "error: " << i << FAIL;
+ tests++;
+ }
+ }
+ cerr << "tested queries: " << tests << endl;
+}
+
+constexpr int N = 10'000'000;
+void performance_test() {
+ timer t;
+ hash_t hash = 0;
+ t.start();
+ hash += josephus(N, N/2);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/knuth.cpp b/test/other/knuth.cpp
new file mode 100644
index 0000000..d469ceb
--- /dev/null
+++ b/test/other/knuth.cpp
@@ -0,0 +1,103 @@
+#include "../util.h"
+constexpr ll inf = LL::INF;
+#include <other/knuth.cpp>
+
+vector<vector<ll>> gen(int n) {
+ vector<vector<ll>> res(n, vector<ll>(n));
+ ll mi = 0;
+ for (ll a = n-1; a >= 0; a--) {
+ for (ll c = n-1; c >= a; c--) {
+ for (ll b = a; b <= c; b++) {
+ for (ll d = c; d < n; d++) {
+ res[a][c] = min(res[a][c], res[a][d] + res[b][c] - res[b][d]);
+ }
+ }
+ res[a][c] -= Random::integer<ll>(0, 1000);
+ mi = min(mi, res[a][c]);
+ }
+ }
+ for (auto& v : res) for (auto& x : v) x -= mi;
+
+ for (ll a = 0; a < n; a++) {
+ for (ll b = a; b < n; b++) {
+ for (ll c = b; c < n; c++) {
+ for (ll d = c; d < n; d++) {
+ if (res[a][d] < 0 || res[a][d] + res[b][c] < res[a][c] + res[b][d]) {
+ cerr << "invalid C array!" << FAIL;
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+vector<vector<ll>> genQuick(int n) {
+ vector<vector<ll>> res(n, vector<ll>(n));
+ for (ll a = n-1; a >= 0; a--) {
+ for (ll c = n-1; c >= a; c--) {
+ res[a][c] = (c-a) * (c - a) + Random::integer<ll>(0, 2);
+ }
+ }
+ return res;
+}
+
+/*ll naive(int n, int m, const vector<vector<ll>>& C) {
+ vector<vector<ll>> state(m+1, vector<ll>(n+1, inf));
+ state[0][0] = 0;
+ for (int i = 1; i <= m; i++) {
+ for (int j = 1; j <= n; j++) {
+ for (int k = 1; k <= j; k++) {
+ state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]);
+ }
+ }
+ }
+ return state[m][n];
+}*/
+
+vector<ll> naive(int n, const vector<vector<ll>>& C) {
+ vector<vector<ll>> state(n+1, vector<ll>(n+1, inf));
+ state[0][0] = 0;
+ vector<ll> res(n+1, inf);
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= n; j++) {
+ for (int k = 1; k <= j; k++) {
+ state[i][j] = min(state[i][j], state[i-1][k-1] + C[k-1][j-1]);
+ }
+ }
+ res[i] = state[i][n];
+ }
+ return res;
+}
+
+void stress_test() {
+ ll tests = 0;
+ for (ll i = 0; i < 1000; i++) {
+ auto n = Random::integer(10, 20);
+ auto C = gen(n);
+ auto expected = naive(n, C);
+ for (ll m = 1; m <= n; m++) {
+ auto got = calc(n, m, C);
+ if (got != expected[m]) cerr << "got: " << got << ", expected: " << expected[m] << FAIL;
+ tests++;
+ }
+ }
+ cerr << "tested random queries: " << tests << endl;
+}
+
+constexpr int N = 5000;
+void performance_test() {
+ timer t;
+ auto C = genQuick(N);
+ t.start();
+ auto hash = calc(N, N/2, C);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/sos.cpp b/test/other/sos.cpp
new file mode 100644
index 0000000..f3a6109
--- /dev/null
+++ b/test/other/sos.cpp
@@ -0,0 +1,50 @@
+#include "../util.h"
+
+vector<ll> sos(const vector<ll>& in) {
+ #include <other/sos.cpp>
+ return res;
+}
+
+vector<ll> naive(const vector<ll>& in) {
+ vector<ll> res(sz(in));
+ for (ll i = 0; i < sz(in); i++) {
+ for (ll j = 0; j <= i; j++) {
+ if ((i | j) == i) {
+ res[i] += in[j];
+ }
+ }
+ }
+ return res;
+}
+
+void stress_test() {
+ ll tests = 0;
+ for (ll i = 0; i < 1000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto in = Random::integers<ll>(n, -1000, 1000);
+ auto got = sos(in);
+ auto expected = naive(in);
+ if (got != expected) cerr << "error: " << i << FAIL;
+ tests += n;
+ }
+ cerr << "tested random queries: " << tests << endl;
+}
+
+constexpr int N = 10'000'000;
+void performance_test() {
+ timer t;
+ auto in = Random::integers<ll>(N, -1000, 1000);
+ t.start();
+ auto res = sos(in);
+ t.stop();
+ hash_t hash = 0;
+ for (ll x : res) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
+
diff --git a/test/other/split.cpp b/test/other/split.cpp
new file mode 100644
index 0000000..e0f5ee1
--- /dev/null
+++ b/test/other/split.cpp
@@ -0,0 +1,24 @@
+#include "../util.h"
+#include <other/split.cpp>
+
+vector<string> split2(string_view s, string_view delim) {
+ vector<string> res;
+ while (!s.empty()) {
+ auto end = s.find_first_of(delim);
+ if (end != 0) res.emplace_back(s.substr(0, end));
+ if (end == string_view::npos) break;
+ s.remove_prefix(end + 1);
+ }
+ return res;
+}
+
+int main() {
+ auto in = "+" + Random::string(100, "abcdef+-*") + "-";
+
+ auto expected = split2(in, "+-*");
+ auto got = split(in, "+-*");
+
+ if (got != expected) cerr << "error" << FAIL;
+ cerr << "done" << endl;
+}
+
diff --git a/test/string/ahoCorasick.cpp b/test/string/ahoCorasick.cpp
new file mode 100644
index 0000000..c3361d6
--- /dev/null
+++ b/test/string/ahoCorasick.cpp
@@ -0,0 +1,76 @@
+#include "../util.h"
+#include <string/ahoCorasick.cpp>
+
+vector<ll> naive(string s, vector<string> patterns) {
+ vector<ll> ans(patterns.size());
+ for (int k = 0; k < (int)patterns.size(); k++) {
+ string pattern = patterns[k];
+ for (int i = 0; i + pattern.size() <= s.size(); i++) {
+ if (s.substr(i, pattern.size()) == pattern) ans[k]++;
+ }
+ }
+ return ans;
+}
+
+vector<ll> normal(string s, vector<string> patterns) {
+ AhoCorasick aho;
+ vector<int> ind(patterns.size());
+ for (int i = 0; i < (int)patterns.size(); i++) {
+ ind[i] = aho.addString(patterns[i]);
+ }
+ aho.buildGraph();
+
+ int v = 0;
+ for (char c : s) v = aho.go(v, c - OFFSET), aho.dp[v]++;
+ aho.dfs();
+ vector<ll> ans(patterns.size());
+ for (int i = 0; i < (int)patterns.size(); i++) {
+ ans[i] = aho.dp[ind[i]];
+ }
+ return ans;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 100; i++) {
+ int n = Random::integer(1, 100);
+ string s = Random::string(n, "abc");
+ int m = Random::integer(1, 100);
+ vector<string> patterns(m);
+ for (string& e : patterns) {
+ int k = Random::integer(1, 100);
+ e = Random::string(k, "abc");
+ }
+
+ auto got = normal(s, patterns);
+ auto expected = naive(s, patterns);
+ if (got != expected) cerr << "Wrong Answer" << FAIL;
+ queries++;
+ }
+ cerr << "Tested random queries: " << queries << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ string s = string(N, 'a') + Random::string(N, "ab");
+ vector<string> patterns = {"a"};
+ for (int sm = 1; sm < N; sm += patterns.back().size()) {
+ patterns.emplace_back(patterns.back().size()+1, 'a');
+ }
+ for (int i = 0; i < 100; i++) {
+ patterns.emplace_back(Random::string(N/100, "ab"));
+ }
+
+ t.start();
+ hash_t hash = normal(s, patterns)[0];
+ t.stop();
+
+ if (t.time > 500) cerr << "Too slow: " << t.time << FAIL;
+ cerr << "Tested performance: " << t.time << "ms (hash: hash " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/deBruijn.cpp b/test/string/deBruijn.cpp
new file mode 100644
index 0000000..6b3fea4
--- /dev/null
+++ b/test/string/deBruijn.cpp
@@ -0,0 +1,43 @@
+#include "../util.h"
+#include <string/lyndon.cpp>
+#include <string/deBruijn.cpp>
+
+bool isDeBruijn(string s, int n, int k) {
+ ll expected = 1;
+ for (ll i = 0; i < n; i++) expected *= k;
+ if (expected != sz(s)) return false;
+ s += s;
+ set<string_view> seen;
+ for (ll i = 0; 2*i < sz(s); i++) {
+ seen.insert(string_view(s).substr(i, n));
+ }
+ return sz(seen) == expected;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 1000; i++) {
+ int n = Random::integer<int>(1, 9);
+ auto [l, r] = Random::pair<char>('b', 'f');
+ auto got = deBruijn(n, l, r);
+ if (!isDeBruijn(got, n, r - l + 1)) cerr << "error" << FAIL;
+ queries += sz(got);
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 26;
+void performance_test() {
+ timer t;
+ t.start();
+ auto res = deBruijn(N, '0', '1');
+ t.stop();
+ hash_t hash = sz(res);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/duval.cpp b/test/string/duval.cpp
new file mode 100644
index 0000000..58b4a44
--- /dev/null
+++ b/test/string/duval.cpp
@@ -0,0 +1,85 @@
+#include "../util.h"
+#pragma GCC diagnostic ignored "-Wreturn-type"
+#include <string/duval.cpp>
+
+constexpr int N = 20'000'000;
+
+bool isLyndon(string_view s) {
+ string t = string(s) + string(s);
+ for (ll i = 1; i < sz(s); i++) {
+ if (s >= t.substr(i, sz(s))) return false;
+ }
+ return !s.empty();
+}
+
+void stress_test_duval() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto s = Random::string(n, "abc");
+ vector<pair<int, int>> got = duval(s);
+ if (got.empty()) cerr << "error: a" << FAIL;
+ if (got.front().first != 0) cerr << "error: b" << FAIL;
+ if (got.back().second != n) cerr << "error: c" << FAIL;
+ for (int j = 1; j < sz(got); j++) {
+ if (got[j - 1].second != got[j].first) cerr << "error: d" << FAIL;
+ }
+ for (auto [l, r] : got) {
+ if (!isLyndon(string_view(s).substr(l, r-l))) cerr << "error: e" << FAIL;
+ }
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test_duval() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ auto got = duval(s);
+ t.stop();
+ hash_t hash = 0;
+ for (auto [l, r] : got) hash += l + r;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int naive(string s) {
+ ll n = sz(s);
+ s += s;
+ int res = 0;
+ for (int i = 0; i < n; i++) {
+ if (string_view(s).substr(i, n) <= string_view(s).substr(res, n)) res = i;
+ }
+ return res;
+}
+
+void stress_test_minrotation() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto s = Random::string(n, "abc");
+ int got = minrotation(s);
+ auto expected = naive(s);
+ if (got != expected) cerr << s << ": got: " << got << ", expected: " << expected << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+void performance_test_minrotation() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ hash_t hash = minrotation(s);
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test_duval();
+ performance_test_duval();
+ stress_test_minrotation();
+ performance_test_minrotation();
+}
diff --git a/test/string/kmp.cpp b/test/string/kmp.cpp
new file mode 100644
index 0000000..9c9c924
--- /dev/null
+++ b/test/string/kmp.cpp
@@ -0,0 +1,85 @@
+#include "../util.h"
+#include <string/kmp.cpp>
+
+vector<int> naive(string_view s) {
+ vector<int> res(sz(s) + 1, -1);
+ for (int i = 0; i < sz(s); i++) {
+ for (int j = 0; j <= i; j++)
+ if (s.substr(0, j) == s.substr(i-j+1, j))
+ res[i+1] = j;
+ }
+ return res;
+}
+
+void stress_test_preprocessing() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 15);
+ auto s = Random::string(n, "abc");
+ auto got = kmpPreprocessing(s);
+ auto expected = naive(s);
+ if (got != expected) cerr << " error" << FAIL;
+ queries += n;
+ }
+ cerr << " tested random queries: " << queries << endl;
+}
+
+constexpr int N = 10'000'000;
+void performance_test_preprocessing() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ auto res = kmpPreprocessing(s);
+ t.stop();
+ hash_t hash = 0;
+ for (int x : res) hash += x;
+ if (t.time > 500) cerr << " too slow: " << t.time << FAIL;
+ cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+vector<int> naive(string_view s, string_view sub) {
+ vector<int> res;
+ auto pos = s.find(sub);
+ while (pos != string_view::npos) {
+ res.push_back(pos);
+ pos = s.find(sub, pos + 1);
+ }
+ return res;
+}
+
+void stress_test_kmp() {
+ ll queries = 0;
+ auto a = Random::string(10'000, "abc");
+ for (int tries = 0; tries < 10'000; tries++) {
+ int n = Random::integer<int>(1, 10);
+ auto b = Random::string(n, "abc");
+ auto got = kmpSearch(a, b);
+ auto expected = naive(a, b);
+ if (got != expected) cerr << " error" << FAIL;
+ queries += got.size();
+ }
+ cerr << " tested random queries: " << queries << endl;
+}
+
+void performance_test_kmp() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ auto sub1 = Random::string(N/2, "a");
+ auto sub2 = Random::string(N/2, "ab");
+ hash_t hash = 0;
+ t.start();
+ hash += kmpSearch(s, sub1).size();
+ hash += kmpSearch(s, sub2).size();
+ t.stop();
+ if (t.time > 500) cerr << " too slow: " << t.time << FAIL;
+ cerr << " tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ cerr << "preprocessing:" << endl;
+ stress_test_preprocessing();
+ performance_test_preprocessing();
+ cerr << "kmp:" << endl;
+ stress_test_kmp();
+ performance_test_kmp();
+}
diff --git a/test/string/longestCommonSubsequence.cpp b/test/string/longestCommonSubsequence.cpp
new file mode 100644
index 0000000..6d7a6c5
--- /dev/null
+++ b/test/string/longestCommonSubsequence.cpp
@@ -0,0 +1,55 @@
+#include "../util.h"
+#include <string/longestCommonSubsequence.cpp>
+
+bool isSubstr(string_view s, string_view sub) {
+ int i = 0;
+ for (char c : s) {
+ if (i < sz(sub) && c == sub[i]) i++;
+ }
+ return i >= sz(sub);
+}
+
+string naive(string_view s, string_view t) {
+ string res = "";
+ for (ll i = 1; i < (1ll << sz(s)); i++) {
+ string tmp;
+ for (ll j = 0; j < sz(s); j++) {
+ if (((i >> j) & 1) != 0) tmp.push_back(s[j]);
+ }
+ if (sz(tmp) >= sz(res) && isSubstr(t, tmp)) res = tmp;
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 12);
+ int m = Random::integer<int>(1, 12);
+ auto s = Random::string(n, "abc");
+ auto t = Random::string(m, "abc");
+ auto got = lcss(s, t);
+ auto expected = naive(s, t);
+ if (got != expected) cerr << s << ", " << t << ", got: " << got << ", expected: " << expected << FAIL;
+ queries += n + m;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 2'000;
+void performance_test() {
+ timer t;
+ auto a = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ auto b = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ auto res = lcss(a, b);
+ t.stop();
+ hash_t hash = sz(res);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/lyndon.cpp b/test/string/lyndon.cpp
new file mode 100644
index 0000000..ecf2dad
--- /dev/null
+++ b/test/string/lyndon.cpp
@@ -0,0 +1,61 @@
+#include "../util.h"
+#include <string/lyndon.cpp>
+
+bool isLyndon(string_view s) {
+ string t = string(s) + string(s);
+ for (ll i = 1; i < sz(s); i++) {
+ if (s >= t.substr(i, sz(s))) return false;
+ }
+ return !s.empty();
+}
+
+vector<string> naive(ll n, char mi, char ma) {
+ vector<string> res;
+ auto dfs = [&](auto&& self, string pref)->void{
+ if (sz(pref) <= n && isLyndon(pref)) res.push_back(pref);
+ if (sz(pref) >= n) return;
+ for (char c = mi; c <= ma; c++) {
+ self(self, pref + c);
+ }
+ };
+ dfs(dfs, "");
+ return res;
+}
+
+vector<string> fast(ll n, char mi, char ma) {
+ vector<string> res;
+ string tmp(1, mi);
+ do {
+ res.push_back(tmp);
+ } while (next(tmp, n, mi, ma));
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (ll i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 6);
+ auto [l, r] = Random::pair<char>('a', 'f');
+ auto got = fast(n, l, r);
+ auto expected = naive(n, l, r);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += sz(expected);
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 9;
+void performance_test() {
+ timer t;
+ t.start();
+ auto res = fast(N, 'a', 'f');
+ t.stop();
+ hash_t hash = sz(res);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/manacher.cpp b/test/string/manacher.cpp
new file mode 100644
index 0000000..503d181
--- /dev/null
+++ b/test/string/manacher.cpp
@@ -0,0 +1,49 @@
+#include "../util.h"
+#include <string/manacher.cpp>
+
+vector<int> naive(string_view s) {
+ vector<int> res(2 * sz(s) + 1);
+ for (int i = 0; i < sz(s); i++) { //odd palindromes
+ int j = 2*i+1;
+ while (i+res[j] < sz(s) && i-res[j] >= 0 && s[i-res[j]] == s[i+res[j]]) res[j]++;
+ res[j]*=2;
+ res[j]--;
+ }
+ for (int i = 0; i <= sz(s); i++) { //even palindromes
+ int j = 2*i;
+ while (i+res[j] < sz(s) && i-res[j]-1 >= 0 && s[i-res[j]-1] == s[i+res[j]]) res[j]++;
+ res[j] *= 2;
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto s = Random::string(n, "abc");
+ vector<int> got = manacher(s);
+ vector<int> expected = naive(s);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 5'000'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ auto got = manacher(s);
+ t.stop();
+ hash_t hash = 0;
+ for (int x : got) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/rollingHash.cpp b/test/string/rollingHash.cpp
new file mode 100644
index 0000000..0491bc0
--- /dev/null
+++ b/test/string/rollingHash.cpp
@@ -0,0 +1,92 @@
+#include "../util.h"
+#include <string/rollingHash.cpp>
+
+string thueMorse(ll n) {
+ string res = "a";
+ while (sz(res) < n) {
+ string tmp = res;
+ for (char& c : tmp) c ^= 1;
+ res += tmp;
+ }
+ return res;
+}
+
+auto getHash(const string& s) {
+ return Hash(s)(0, sz(s));
+}
+
+void testThueMorse() {
+ set<decltype(getHash(""))> got;
+ set<string> expected;
+ string s = thueMorse(1000);
+ Hash h(s);
+ for (int l = 0; l < sz(s); l++) {
+ for (int r = l + 1; r <= sz(s); r++) {
+ got.insert(h(l, r));
+ expected.insert(s.substr(l, r - l));
+ }
+ }
+ if (sz(got) != sz(expected)) cerr << "error: thueMorse" << FAIL;
+ cerr << "thueMorse: ok" << endl;
+}
+
+void testTiny() {
+ if (getHash("aa") == getHash("a")) cerr << "error: tiny" << FAIL;
+ if (getHash("00") == getHash("0")) cerr << "error: tiny" << FAIL;
+ if (getHash("AA") == getHash("A")) cerr << "error: tiny" << FAIL;
+ cerr << "tiny: ok" << endl;
+}
+
+void testSmall() {
+ set<decltype(getHash(""))> got;
+ ll expected = 0;
+ auto dfs = [&](auto&& self, string pref)->void {
+ expected++;
+ got.insert(getHash(pref));
+ if(sz(pref) >= 5) return;
+ for (char c = 'a'; c <= 'z'; c++) {
+ self(self, pref + c);
+ }
+ };
+ dfs(dfs, "");
+ if (sz(got) != expected) cerr << "error: small" << FAIL;
+ cerr << "small: ok" << endl;
+}
+
+void stress_test() {
+ set<decltype(getHash(""))> got;
+ set<string> expected;
+ string s = Random::string(1000, "abc");
+ Hash h(s);
+ for (int l = 0; l < sz(s); l++) {
+ for (int r = l + 1; r <= sz(s); r++) {
+ got.insert(h(l, r));
+ expected.insert(s.substr(l, r - l));
+ }
+ }
+ if (sz(got) != sz(expected)) cerr << "error: stress test" << FAIL;
+ cerr << "stress test: ok" << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ hash_t hash = 0;
+ t.start();
+ Hash h(s);
+ for (ll i = 0; i < N; i++) {
+ hash += h(i, i + 2*N);
+ }
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ testThueMorse();
+ testTiny();
+ testSmall();
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/rollingHashCf.cpp b/test/string/rollingHashCf.cpp
new file mode 100644
index 0000000..79003de
--- /dev/null
+++ b/test/string/rollingHashCf.cpp
@@ -0,0 +1,94 @@
+#include "../util.h"
+#include <string/rollingHashCf.cpp>
+
+constexpr ll RandomQ = 318LL << 53;
+
+string thueMorse(ll n) {
+ string res = "a";
+ while (sz(res) < n) {
+ string tmp = res;
+ for (char& c : tmp) c ^= 1;
+ res += tmp;
+ }
+ return res;
+}
+
+auto getHash(const string& s) {
+ return Hash(s, RandomQ)(0, sz(s));
+}
+
+void testThueMorse() {
+ set<decltype(getHash(""))> got;
+ set<string> expected;
+ string s = thueMorse(1000);
+ Hash h(s, RandomQ);
+ for (int l = 0; l < sz(s); l++) {
+ for (int r = l + 1; r <= sz(s); r++) {
+ got.insert(h(l, r));
+ expected.insert(s.substr(l, r - l));
+ }
+ }
+ if (sz(got) != sz(expected)) cerr << "error: thueMorse" << FAIL;
+ cerr << "thueMorse: ok" << endl;
+}
+
+void testTiny() {
+ if (getHash("aa") == getHash("a")) cerr << "error: tiny" << FAIL;
+ if (getHash("00") == getHash("0")) cerr << "error: tiny" << FAIL;
+ if (getHash("AA") == getHash("A")) cerr << "error: tiny" << FAIL;
+ cerr << "tiny: ok" << endl;
+}
+
+void testSmall() {
+ set<decltype(getHash(""))> got;
+ ll expected = 0;
+ auto dfs = [&](auto&& self, string pref)->void {
+ expected++;
+ got.insert(getHash(pref));
+ if(sz(pref) >= 5) return;
+ for (char c = 'a'; c <= 'z'; c++) {
+ self(self, pref + c);
+ }
+ };
+ dfs(dfs, "");
+ if (sz(got) != expected) cerr << "error: small" << FAIL;
+ cerr << "small: ok" << endl;
+}
+
+void stress_test() {
+ set<decltype(getHash(""))> got;
+ set<string> expected;
+ string s = Random::string(1000, "abc");
+ Hash h(s, RandomQ);
+ for (int l = 0; l < sz(s); l++) {
+ for (int r = l + 1; r <= sz(s); r++) {
+ got.insert(h(l, r));
+ expected.insert(s.substr(l, r - l));
+ }
+ }
+ if (sz(got) != sz(expected)) cerr << "error: stress test" << FAIL;
+ cerr << "stress test: ok" << endl;
+}
+
+constexpr int N = 1'000'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ hash_t hash = 0;
+ t.start();
+ Hash h(s, RandomQ);
+ for (ll i = 0; i < N; i++) {
+ hash += h(i, i + 2*N);
+ }
+ t.stop();
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ testThueMorse();
+ testTiny();
+ testSmall();
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/suffixArray.cpp b/test/string/suffixArray.cpp
new file mode 100644
index 0000000..4945d8e
--- /dev/null
+++ b/test/string/suffixArray.cpp
@@ -0,0 +1,61 @@
+#include "../util.h"
+#include <string/suffixArray.cpp>
+
+vector<int> naive(string_view s) {
+ vector<int> SA(sz(s));
+ iota(all(SA), 0);
+ sort(all(SA), [s](int a, int b){
+ return s.substr(a) < s.substr(b);
+ });
+ return SA;
+}
+
+int lcp(string_view s, int x, int y) {
+ int res = 0;
+ while (x + res < sz(s) && y + res < sz(s) && s[x + res] == s[y + res]) res++;
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 100; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto s = Random::string(n, "abc");
+ SuffixArray sa(s);
+ vector<int> got = sa.SA;
+ vector<int> expected = naive(s);
+ vector<int> SA(n);
+ if (got != expected) cerr << "error: SA" << FAIL;
+ got = sa.LCP;
+ swap(SA, expected);
+ for (int x = 0; x < n; x++) {
+ for (int y = 0; y < n; y++) {
+ int gotLCP = sa.lcp(x, y);
+ int expectedLCP = lcp(s, x, y);
+ if (gotLCP != expectedLCP) cerr << "error: lcp" << FAIL;
+ }
+ if (x > 0) expected[x] = lcp(s, SA[x-1], SA[x]);
+ }
+ if (got != expected) cerr << "error: LCP" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 200'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ SuffixArray sa(s);
+ t.stop();
+ hash_t hash = 0;
+ for (int i = 0; i < sz(sa.SA); i++) hash += i*sa.SA[i];
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/suffixAutomaton.cpp b/test/string/suffixAutomaton.cpp
new file mode 100644
index 0000000..c2ff511
--- /dev/null
+++ b/test/string/suffixAutomaton.cpp
@@ -0,0 +1,62 @@
+#include "../util.h"
+#include <string/suffixAutomaton.cpp>
+
+pair<int, int> naive(string_view s, string_view t) {
+ int pos = 0;
+ int len = 0;
+ for (int j = 0; j < sz(t); j++) {
+ for (int i = 0; i < sz(s); i++) {
+ int cur = 0;
+ while (i+cur < sz(s) && j+cur < sz(t) && s[i+cur] == t[j+cur]) cur++;
+ if (cur > len) {
+ pos = j;
+ len = cur;
+ }
+ }
+ }
+ return {pos, len};
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 1000; i++) {
+ int n = Random::integer<int>(1, 100);
+ auto s = Random::string(n, "abc");
+ SuffixAutomaton sa(s);
+ for (int j = 0; j < 1000; j++) {
+ int m = Random::integer<int>(1, 100);
+ auto t = Random::string(m, "abc");
+ auto got = sa.longestCommonSubstring(t);
+ auto expected = naive(s, t);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += m;
+ }
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 500'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyz");
+ t.start();
+ SuffixAutomaton sa(s);
+ t.stop();
+ hash_t hash = 0;
+ for (ll c = 0; c < sz(s);) {
+ int m = Random::integer<int>(1, 1000);
+ s = Random::string(m, "abc");
+ t.start();
+ auto [p, l] = sa.longestCommonSubstring(s);
+ t.stop();
+ hash += l + p;
+ c += m;
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/suffixTree.cpp b/test/string/suffixTree.cpp
new file mode 100644
index 0000000..c0d79e4
--- /dev/null
+++ b/test/string/suffixTree.cpp
@@ -0,0 +1,50 @@
+#include "../util.h"
+#include <string/suffixTree.cpp>
+
+vector<string> naive(string_view s) {
+ vector<string> res(sz(s));
+ for (ll i = 0; i < sz(s); i++) {
+ res[i] = s.substr(i);
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int i = 0; i < 10'000; i++) {
+ int n = Random::integer<int>(1, 15);
+ auto s = Random::string(n, "abc") + "#";
+ SuffixTree st(s);
+ vector<string> got(n + 1);
+ auto dfs = [&](auto&& self, string pref, ll node) -> void {
+ auto& [l, r, _, next] = st.tree[node];
+ if (l >= 0) pref += s.substr(l, r - l);
+ if (pref.back() == '#') got[n + 1 - sz(pref)] = pref;
+ for (auto [__, j] : next) {
+ self(self, pref, j);
+ }
+ };
+ dfs(dfs, "", 0);
+ auto expected = naive(s);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 200'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ SuffixTree st(s);
+ t.stop();
+ hash_t hash = sz(st.tree);
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/trie.cpp b/test/string/trie.cpp
new file mode 100644
index 0000000..45d89cf
--- /dev/null
+++ b/test/string/trie.cpp
@@ -0,0 +1,58 @@
+#include "../util.h"
+#include <string/trie.cpp>
+
+void stress_test() {
+ multiset<vector<int>> naive;
+ ll queries = 0;
+ ll deleted = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ {
+ int n = Random::integer<int>(1, 20);
+ auto s = Random::integers<int>(n, 0, 2);
+ insert(s);
+ naive.insert(s);
+ }
+ {
+ int n = Random::integer<int>(1, 20);
+ auto s = Random::integers<int>(n, 0, 2);
+ bool got = erase(s);
+ auto it = naive.find(s);
+ bool expected = it != naive.end();
+ if (expected) naive.erase(it);
+ if (got != expected) cerr << "error" << FAIL;
+ queries++;
+ if (got) deleted++;
+ }
+ }
+ cerr << "tested random queries: " << queries << " (" << deleted << ")" << endl;
+}
+
+constexpr int N = 10'000;
+void performance_test() {
+ timer t;
+ trie = {node()};
+ hash_t hash = 0;
+ for (int tries = 0; tries < N; tries++) {
+ {
+ int n = Random::integer<int>(1, 2000);
+ auto s = Random::integers<int>(n, 0, 2);
+ t.start();
+ insert(s);
+ t.stop();
+ }
+ {
+ int n = Random::integer<int>(1, 2000);
+ auto s = Random::integers<int>(n, 0, 2);
+ t.start();
+ hash += erase(s);
+ t.stop();
+ }
+ }
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/string/z.cpp b/test/string/z.cpp
new file mode 100644
index 0000000..f890a3e
--- /dev/null
+++ b/test/string/z.cpp
@@ -0,0 +1,41 @@
+#include "../util.h"
+#include <string/z.cpp>
+
+vector<int> naive(const string& s) {
+ vector<int> res(sz(s));
+ for (int i = 1; i < sz(s); i++) {
+ while (i + res[i] < sz(s) && s[res[i]] == s[i + res[i]]) res[i]++;
+ }
+ return res;
+}
+
+void stress_test() {
+ ll queries = 0;
+ for (int tries = 0; tries < 100'000; tries++) {
+ int n = Random::integer<int>(1, 15);
+ auto s = Random::string(n, "abc");
+ auto got = Z(s);
+ auto expected = naive(s);
+ if (got != expected) cerr << "error" << FAIL;
+ queries += n;
+ }
+ cerr << "tested random queries: " << queries << endl;
+}
+
+constexpr int N = 10'000'000;
+void performance_test() {
+ timer t;
+ auto s = Random::string(N, "a") + Random::string(N, "ab") + Random::string(N, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#");
+ t.start();
+ auto res = Z(s);
+ t.stop();
+ hash_t hash = 0;
+ for (int x : res) hash += x;
+ if (t.time > 500) cerr << "too slow: " << t.time << FAIL;
+ cerr << "tested performance: " << t.time << "ms (hash: " << hash << ")" << endl;
+}
+
+int main() {
+ stress_test();
+ performance_test();
+}
diff --git a/test/template/template.cpp b/test/template/template.cpp
new file mode 100644
index 0000000..db9aa00
--- /dev/null
+++ b/test/template/template.cpp
@@ -0,0 +1 @@
+#include <template/template.cpp>
diff --git a/test/test.sh b/test/test.sh
new file mode 100755
index 0000000..0ca230b
--- /dev/null
+++ b/test/test.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+set -e
+cd "$(dirname "$0")"
+ulimit -s 4000000
+export MALLOC_PERTURB_="$((2#01011001))"
+
+declare -A cppstandard
+cppstandard["string/suffixArray.cpp"]="gnu++20"
+
+test_file() {
+ file=$(realpath --relative-to="${PWD}" "${1}")
+ echo "$file:"
+ echo "compiling..."
+ std="gnu++17"
+ if [[ -v cppstandard[$file] ]]; then
+ std=${cppstandard[$file]}
+ fi
+ g++ -std=$std "$file" -I ../content/ -O2 -Wall -Wextra -Wshadow -Werror
+ echo "running..."
+ timeout --foreground 60s ./a.out
+ echo ""
+ rm ./a.out
+}
+
+list_missing() {
+ declare -A ignore
+ ignore["datastructures/stlPriorityQueue.cpp"]=1
+ ignore["datastructures/stlRope.cpp"]=1
+ ignore["other/bitOps.cpp"]=1
+ ignore["other/pbs.cpp"]=1
+ ignore["other/pragmas.cpp"]=1
+ ignore["other/stuff.cpp"]=1
+ ignore["other/timed.cpp"]=1
+ ignore["tests/gcc5bug.cpp"]=1
+ ignore["tests/precision.cpp"]=1
+ ignore["tests/whitespace.cpp"]=1
+
+ echo "missing tests:"
+ find ../content/ -type f -name '*.cpp' -print0 | sort -z | while read -d $'\0' file
+ do
+ file=${file#../content/}
+ if [ ! -f "$file" ] && [[ ! -v ignore["$file"] ]]; then
+ echo " $file"
+ fi
+ done
+}
+
+if [ "$#" -ne 0 ]; then
+ for arg in "$@"
+ do
+ if [[ "$arg" == "--missing" ]]; then
+ list_missing
+ elif [ -d "$arg" ]; then
+ dir=$(realpath --relative-to="${PWD}" "$arg")
+ find . -type f -path "./${dir}/*.cpp" -print0 | sort -z | while read -d $'\0' file
+ do
+ test_file "$file"
+ done
+ elif [ -f "$arg" ]; then
+ test_file "$arg"
+ fi
+ done
+else
+ find . -type f -path '*.cpp' -print0 | sort -z | while read -d $'\0' file
+ do
+ test_file "$file"
+ done
+ list_missing
+fi
+
diff --git a/test/util.h b/test/util.h
new file mode 100644
index 0000000..bfa35d8
--- /dev/null
+++ b/test/util.h
@@ -0,0 +1,411 @@
+#include <bits/stdc++.h>
+using namespace std;
+
+#define all(x) std::begin(x), std::end(x)
+#define sz(x) (ll)std::size(x)
+
+using ll = long long;
+using lll = __int128;
+using ld = long double;
+using hash_t = unsigned long long;
+
+namespace INT {constexpr int INF = 0x3FFF'FFFF;}
+namespace LL {constexpr ll INF = 0x3FFF'FFFF'FFFF'FFFFll;}
+namespace LD {constexpr ld INF = numeric_limits<ld>::infinity();}
+
+namespace details {
+ template<typename T = ll>
+ bool isPrime(T x) {
+ for (T i = 2; i*i <= x; i++) {
+ if (x % i == 0) return false;
+ }
+ return x > 1;
+ }
+}
+
+namespace Random {
+ mt19937_64 rng(3141592653589793238ll);
+ template<typename T = ll>
+ T integer(T l, T r) {
+ return uniform_int_distribution<T>(l, r-1)(rng);
+ }
+
+ template<typename T = ll>
+ T integer(T r) {
+ return integer<T>(0, r);
+ }
+
+ template<typename T = ll>
+ std::vector<T> integers(std::size_t n, T l, T r) {
+ std::vector<T> res(n);
+ for (T& x : res) x = integer<T>(l, r);
+ return res;
+ }
+
+ template<typename T = ll>
+ std::vector<T> integers(std::size_t n, T r) {
+ return integers<T>(n, 0, r);
+ }
+
+ template<typename T = ll>
+ std::vector<T> distinct(std::size_t n, T l, T r) {
+ std::map<T, T> used;
+ std::vector<T> res;
+ for (std::size_t i = 0; i < n; i++) {
+ T x = integer<T>(l, r - i);
+ auto it = used.find(x);
+ if (it != used.end()) res.emplace_back(it->second);
+ else res.emplace_back(x);
+ it = used.find(r - i - 1);
+ if (it != used.end()) used[x] = it->second;
+ else used[x] = r - i - 1;
+ }
+ return res;
+ }
+
+ template<typename T = ll>
+ std::vector<T> distinct(std::size_t n, T r) {
+ return distinct<T>(n, 0, r);
+ }
+
+ template<typename T = ld>
+ T real(T l, T r) {
+ return uniform_real_distribution<T>(l, r)(rng);
+ }
+
+ template<typename T = ld>
+ T real(T r) {
+ return real<T>(0, r);
+ }
+
+ template<typename T = ld>
+ std::vector<T> reals(std::size_t n, T l, T r) {
+ std::vector<T> res(n);
+ for (T& x : res) x = real<T>(l, r);
+ return res;
+ }
+
+ template<typename T = ld>
+ std::vector<T> reals(std::size_t n, T r) {
+ return reals<T>(n, 0, r);
+ }
+
+ template<typename T = ll>
+ std::pair<T, T> pair(T l, T r) {
+ T a = integer<T>(l, r);
+ T b = integer<T>(l, r);
+ if (a > b) swap(a, b);
+ return {a, b};
+ }
+
+ template<typename T = ll>
+ std::pair<T, T> pair(T r) {
+ return pair<T>(0, r);
+ }
+
+ std::string string(std::size_t n, string_view chars) {
+ std::string res(n, '*');
+ for (char& c : res) c = chars[integer(sz(chars))];
+ return res;
+ }
+
+ template<typename T = ll>
+ T prime(T l, T r) {
+ T res = 0;
+ do {
+ res = integer<T>(l, r);
+ } while (!details::isPrime<T>(res));
+ return res;
+ }
+
+ template<typename T = ll>
+ T prime(T r) {
+ return prime<T>(2, r);
+ }
+
+ template<typename T = ll, typename std::enable_if<!std::is_floating_point<T>::value>::type* = nullptr>
+ std::complex<T> point(T l, T r) {
+ return {integer<T>(l, r), integer<T>(l, r)};
+ }
+
+ template<typename T = ld, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+ std::complex<T> point(T l, T r) {
+ return {real(l, r), real(l, r)};
+ }
+
+ template<typename T = ll>
+ std::complex<T> point(T r) {
+ return point<T>(0, r);
+ }
+
+ template<typename T = ll, typename std::enable_if<!std::is_floating_point<T>::value>::type* = nullptr>
+ std::vector<std::complex<T>> points(std::size_t n, T l, T r) {
+ std::vector<std::complex<T>> res(n);
+ for (auto& x : res) x = point<T>(l, r);
+ return res;
+ }
+
+ template<typename T = ld, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+ std::vector<std::complex<T>> points(std::size_t n, T l, T r) {
+ std::vector<std::complex<T>> res(n);
+ for (auto& x : res) x = point<T>(l, r);
+ return res;
+ }
+
+ template<typename T = ll>
+ std::vector<std::complex<T>> points(std::size_t n, T r) {
+ return points<T>(n, 0, r);
+ }
+
+}
+
+[[noreturn]] ostream& FAIL(ostream& os) {
+ os << endl;
+ exit(1);
+}
+
+struct timer {
+ bool running = false;
+ double time = 0;
+ chrono::steady_clock::time_point begin;
+
+ void start() {
+ if (running) cerr << "timer already running!" << FAIL;
+ running = true;
+ begin = chrono::steady_clock::now();
+ }
+
+ void stop() {
+ auto end = chrono::steady_clock::now();
+ if (!running) cerr << "timer not running!" << FAIL;
+ running = false;
+ time += chrono::duration_cast<chrono::duration<double, milli>>(end - begin).count();
+ }
+
+ void reset() {
+ running = false;
+ time = 0;
+ }
+
+ ~timer() {
+ if (running) cerr << "timer not stopped!" << FAIL;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const timer& t) {
+ if (t.running) cerr << "timer already running!" << FAIL;
+ return os << t.time;
+ }
+};
+
+namespace c20 {
+ namespace detail {
+ template<class T, std::size_t N, std::size_t... I>
+ constexpr std::array<std::remove_cv_t<T>, N> to_array_impl(T (&a)[N], std::index_sequence<I...>) {
+ return {{a[I]...}};
+ }
+ }
+
+ template<class T, std::size_t N>
+ constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]) {
+ return c20::detail::to_array_impl(a, std::make_index_sequence<N>{});
+ }
+}
+
+struct NoData {};
+
+template<typename W = NoData>
+struct Edge {
+ int from, to;
+ mutable W w;
+
+ Edge(int from_, int to_, W w_ = {}) : from(from_), to(to_), w(w_) {}
+
+ template<typename F>
+ void forE(int x, F&& f) const {
+ int y = from ^ to ^ x;
+ if constexpr (is_same_v<W, NoData>) f(x, y);
+ else f(x, y, w);
+ }
+
+ template<typename F>
+ void forE(F&& f) const {
+ forE(from, f);
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Edge& e) {
+ os << e.from << "->" << e.to;
+ if constexpr (!is_same_v<W, NoData>) os << ": " << e.w;
+ return os;
+ }
+};
+
+
+template<typename W = NoData, bool DIR = false, bool MULTI = false, bool LOOPS = false>
+class Graph {
+ vector<multimap<int, int>> adj;
+ vector<Edge<W>> edges;
+
+ void _addAdj(int e) {
+ adj[edges[e].from].emplace(edges[e].to, e);
+ if (!DIR && edges[e].from != edges[e].to) adj[edges[e].to].emplace(edges[e].from, e);
+ }
+public:
+
+ Graph(int n) : adj(n) {}
+
+ int m() const {return sz(edges);}
+ int n() const {return sz(adj);}
+ int deg(int x) const {return sz(adj[x]);}
+
+ Graph& clear() {
+ adj.assign(adj.size(), {});
+ edges.clear();
+ return *this;
+ }
+
+ bool addEdge(int from, int to, W w = {}) {
+ if (!LOOPS && from == to) return false;
+ if (!MULTI && adj[from].find(to) != adj[from].end()) return false;
+ edges.emplace_back(from, to, w);
+ _addAdj(sz(edges) - 1);
+ return true;
+ }
+
+ Graph& reverse() {
+ for (auto& e : edges) swap(e.from, e.to);
+ adj.assign(adj.size(), {});
+ for (int i = 0; i < sz(edges); i++) _addAdj(i);
+ return *this;
+ }
+
+ Graph& shuffle() {
+ std::shuffle(all(edges), Random::rng);
+ if constexpr (!DIR) {
+ for (auto& e : edges) {
+ if (Random::integer(0, 2)) swap(e.from, e.to);
+ }
+ }
+ adj.assign(adj.size(), {});
+ for (int i = 0; i < sz(edges); i++) _addAdj(i);
+ return *this;
+ }
+
+ Graph& permutate() {
+ vector<int> perm(n());
+ iota(all(perm), 0);
+ std::shuffle(all(perm), Random::rng);
+ for (auto& e : edges) {
+ e.from = perm[e.from];
+ e.to = perm[e.to];
+ }
+ shuffle();
+ return *this;
+ }
+
+ template<typename F>
+ void forEdges(F&& f) {
+ for (auto e : edges) e.forE(f);
+ }
+
+ template<typename F>
+ void forOut(int x, F&& f) {
+ for (auto [_, id] : adj[x]) edges[id].forE(x, f);
+ }
+
+ Graph& tree() {
+ if (DIR) cerr << "randomTree must be undirected" << FAIL;
+ if (n() <= 1) return *this;
+ auto code = Random::integers<int>(n()-2, 0, n());
+ vector<int> count(n());
+ for (int x : code) count[x]++;
+ int current = -1;
+ int skip = -1;
+ auto getNext = [&](){
+ int oldSkip = skip;
+ skip = -1;
+ if (0 <= oldSkip and oldSkip <= current) return oldSkip;
+ for (current++; count[current] > 0; current++) {}
+ return current;
+ };
+ for (int x : code) {
+ addEdge(x, getNext());
+ count[x]--;
+ if (count[x] == 0) skip = x;
+ }
+ addEdge(getNext(), getNext());
+ return *this;
+ }
+
+ Graph& star() {
+ for (int i = 1; i < n; i++) addEdge(0, i);
+ return *this;
+ }
+
+ Graph& path() {
+ for (int i = 1; i < n; i++) addEdge(i-1, i);
+ return *this;
+ }
+
+ Graph& erdosRenyi(int todo) {//this could be slow...
+ if constexpr (!MULTI) {
+ ll lim = (ll)n() * (n() - 1) / 2;
+ if constexpr (DIR) lim *= 2;
+ if constexpr (LOOPS) lim += n;
+ lim -= m();
+ if (todo > lim) cerr << "too many edges! n: " << n() << " " << todo << " > " << lim << FAIL;
+ if (todo > lim / 2) {
+ vector<pair<int, int>> tmp;
+ if constexpr(LOOPS) {
+ for (int i = 0; i < n(); i++) tmp.emplace_back(i, i);
+ }
+ for (int i = 0; i < n(); i++) {
+ for (int j = 0; j < i; j++) {
+ tmp.emplace_back(i, j);
+ if constexpr (DIR) tmp.emplace_back(j, i);
+ }
+ }
+ if constexpr (!DIR) {
+ for (auto& [a, b] : tmp) {
+ if (Random::integer<int>(0, 2) == 0) {
+ swap(a, b);
+ }
+ }
+ }
+ std::shuffle(all(tmp), Random::rng);
+ for (auto [a, b] : tmp) {
+ if (todo <= 0) break;
+ if (addEdge(a, b)) todo--;
+ }
+ if (todo > 0) cerr << "too many edges!" << FAIL;
+ return *this;
+ }
+ }
+ while (todo > 0) {
+ int a = Random::integer(0, n());
+ int b = Random::integer(0, n());
+ if (addEdge(a, b)) todo--;
+ }
+ return *this;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Graph& g) {
+ os << g.n() << " " << g.m();
+ for (auto& e : g.edges) os << endl << e;
+ return os;
+ }
+};
+
+ld float_error(ld given, ld expected) {
+ if (isfinite(given) && isfinite(expected)) {
+ ld absDiff = abs(given-expected);
+ ld relDiff = abs((given-expected)/expected);
+ return min(absDiff, relDiff);
+ }
+ if (isnan(given) && isnan(expected)) {
+ return 0;
+ }
+ if (isinf(given) && isinf(expected)) {
+ return signbit(given) == signbit(expected) ? 0 : numeric_limits<ld>::infinity();
+ }
+ return numeric_limits<ld>::infinity();
+}