├── tests ├── test_convolution.py ├── test_acl_string.py ├── test_dsu.py ├── test_segtree.py ├── test_lazysegtree.py ├── test_basic.py ├── test_fps.py ├── test_fenwicktree.py ├── test_two_sat.py ├── test_acl_math.py ├── test_mincostflow.py ├── test_maxflow.py ├── test_scc.py └── test_prime_fact.py ├── fenwicktree.py ├── .github └── workflows │ ├── format.yml │ ├── test.yml │ ├── python-package.yml │ ├── release.yml │ └── ci.yml ├── README.md ├── LICENSE ├── acl_math.py ├── dsu.py ├── scc.py ├── prime_fact.py ├── two_sat.py ├── pyproject.toml ├── segtree.py ├── .gitignore ├── mincostflow.py ├── maxflow.py ├── acl_string.py ├── lazysegtree.py ├── convolution.py └── fps.py /tests/test_convolution.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import convolution 10 | 11 | 12 | class TestConvolution(unittest.TestCase): 13 | """Test cases for convolution module""" 14 | 15 | def practice2_f(self, N, M, A, B, ans): 16 | CONV = convolution.FFT(998244353) 17 | self.assertEqual(CONV.convolution(A, B), ans) 18 | 19 | def test_practice2_f(self): 20 | self.practice2_f( 21 | 4, 5, [1, 2, 3, 4], [5, 6, 7, 8, 9], [5, 16, 34, 60, 70, 70, 59, 36] 22 | ) 23 | self.practice2_f(1, 1, [10000000], [10000000], [871938225]) 24 | 25 | 26 | if __name__ == "__main__": 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /fenwicktree.py: -------------------------------------------------------------------------------- 1 | class fenwick_tree: 2 | n = 1 3 | data = [0 for i in range(n)] 4 | 5 | def __init__(self, N): 6 | self.n = N 7 | self.data = [0 for i in range(N)] 8 | 9 | def add(self, p, x): 10 | assert 0 <= p < self.n, "0<=p 0: 25 | s += self.data[r - 1] 26 | r -= r & -r 27 | return s 28 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Auto Format 2 | 3 | on: 4 | pull_request: 5 | branches: [ master, main ] 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | format: 12 | runs-on: ubuntu-latest 13 | if: github.event.pull_request.head.repo.full_name == github.repository 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: '3.x' 22 | - name: Install black 23 | run: pip install black 24 | - name: Format code 25 | run: black . 26 | - name: Commit changes 27 | uses: stefanzweifel/git-auto-commit-action@v5 28 | with: 29 | commit_message: "chore: format code" 30 | branch: ${{ github.head_ref }} 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ACL-for-python 2 | 3 | AC-libraryをpython版に書き換えたものです 4 | 5 | 本家ACLのリンク: 6 | https://github.com/atcoder/ac-library 7 | 8 | ## 使用環境 9 | python3系 10 | 11 | 2020年の言語アップデート以降のAtCoderの環境(PyPy 7.3.0 with GCC 7.4.0)で動くことを確認済みです。 12 | 13 | 特にAtCoder Library Practice Contest(https://atcoder.jp/contests/practice2 )をPyPyで全問ACできることを確認済みです。 14 | 15 | ## 使用方法 16 | 17 | [Wiki](https://github.com/shakayami/ACL-for-python/wiki)を参照 18 | 19 | 関数の名前も本家と合わせているためほぼ同じような使い方ができると思います。 20 | ## インストール方法 21 | ソースコードをコピペするなりしてください。 22 | 23 | ## なにかあったときの連絡先 24 | バグがある、こう書いたほうが速い…等 25 | 26 | - twitter : @shakayami_kpro 27 | - githubのissue 28 | - githubのpull request 29 | 30 | バグは発見次第修正される予定です。 31 | 32 | ## LICENCE 33 | [The Unlicense](https://github.com/shakayami/ACL-for-python/blob/master/LICENSE) 34 | 35 | 想定される使用方法 36 | - 競技プログラミング目的の使用 37 | - 競技プログラミング以外の目的の使用 38 | - AIがプログラミングするための学習用データにする 39 | - 個人的に参考にする 40 | - 商用利用 41 | 42 | etc... 43 | 44 | 使用方法は他にもいくらでも考えられますが、好きに使ってください。 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -e . 28 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 29 | 30 | - name: Run tests 31 | run: | 32 | python -m unittest discover -s tests -p "test_*.py" -v 33 | 34 | - name: Run linting (if available) 35 | run: | 36 | if pip list | grep -q black; then 37 | black --check . 38 | fi 39 | if pip list | grep -q flake8; then 40 | flake8 . 41 | fi 42 | continue-on-error: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /tests/test_acl_string.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import acl_string 10 | 11 | 12 | class TestACLString(unittest.TestCase): 13 | """Test cases for acl_string module""" 14 | 15 | def practice2_i(self, S, answer): 16 | sa = acl_string.string.suffix_array(S) 17 | res = (len(S) * (len(S) + 1)) // 2 18 | for x in acl_string.string.lcp_array(S, sa): 19 | res -= x 20 | self.assertEqual(res, answer) 21 | 22 | def test_practice2_i(self): 23 | self.practice2_i("abcbcba", 21) 24 | self.practice2_i("mississippi", 53) 25 | self.practice2_i("ababacaca", 33) 26 | self.practice2_i("aaaaa", 5) 27 | 28 | def test_suffix_array(self): 29 | """Test suffix array functionality""" 30 | # TODO: Add test cases for suffix array 31 | pass 32 | 33 | def test_z_algorithm(self): 34 | """Test Z algorithm functionality""" 35 | # TODO: Add test cases for Z algorithm 36 | pass 37 | 38 | def test_lcp_array(self): 39 | """Test LCP array functionality""" 40 | # TODO: Add test cases for LCP array 41 | pass 42 | 43 | 44 | if __name__ == "__main__": 45 | unittest.main() 46 | -------------------------------------------------------------------------------- /tests/test_dsu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import dsu 10 | 11 | 12 | class TestDSU(unittest.TestCase): 13 | """Test cases for dsu module""" 14 | 15 | def test_dsu_basic(self): 16 | """Test basic DSU operations""" 17 | d = dsu.dsu(5) 18 | d.merge(0, 1) 19 | d.merge(2, 3) 20 | self.assertTrue(d.same(0, 1)) 21 | self.assertFalse(d.same(0, 2)) 22 | self.assertEqual(d.size(0), 2) 23 | self.assertEqual(d.size(2), 2) 24 | 25 | def practice2_a(self, N, Q, query, ans): 26 | G = dsu.dsu(N) 27 | res = [] 28 | for t, u, v in query: 29 | if t == 0: 30 | G.merge(u, v) 31 | else: 32 | res.append(1 if G.same(u, v) else 0) 33 | self.assertEqual(ans, res) 34 | 35 | def test_practice2_a(self): 36 | self.practice2_a( 37 | 4, 38 | 7, 39 | [ 40 | (1, 0, 1), 41 | (0, 0, 1), 42 | (0, 2, 3), 43 | (1, 0, 1), 44 | (1, 1, 2), 45 | (0, 0, 2), 46 | (1, 1, 3), 47 | ], 48 | [0, 1, 0, 1], 49 | ) 50 | 51 | 52 | if __name__ == "__main__": 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /tests/test_segtree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import segtree 10 | 11 | 12 | class TestSegTree(unittest.TestCase): 13 | """Test cases for segtree module""" 14 | 15 | def practice2_j(self, N, Q, A, query, ans): 16 | G = segtree.segtree([i for i in A], max, -1) 17 | res = [] 18 | for i in range(Q): 19 | t, a, b = query[i] 20 | if t == 1: 21 | x, v = a, b 22 | x -= 1 23 | G.set(x, v) 24 | if t == 2: 25 | l, r = a, b 26 | l -= 1 27 | r -= 1 28 | res.append(G.prod(l, r + 1)) 29 | if t == 3: 30 | x, v = a, b 31 | x -= 1 32 | 33 | def f(t): 34 | if v > t: 35 | return True 36 | else: 37 | return False 38 | 39 | res.append(G.max_right(x, f) + 1) 40 | self.assertEqual(res, ans) 41 | 42 | def test_practice2_j(self): 43 | self.practice2_j( 44 | 5, 45 | 5, 46 | [1, 2, 3, 2, 1], 47 | [(2, 1, 5), (3, 2, 3), (1, 3, 1), (2, 2, 4), (3, 1, 3)], 48 | [3, 3, 2, 6], 49 | ) 50 | 51 | 52 | if __name__ == "__main__": 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /acl_math.py: -------------------------------------------------------------------------------- 1 | def inv_gcd(a, b): 2 | a = a % b 3 | if a == 0: 4 | return (b, 0) 5 | s = b 6 | t = a 7 | m0 = 0 8 | m1 = 1 9 | while t: 10 | u = s // t 11 | s -= t * u 12 | m0 -= m1 * u 13 | s, t = t, s 14 | m0, m1 = m1, m0 15 | if m0 < 0: 16 | m0 += b // s 17 | return (s, m0) 18 | 19 | 20 | def inv_mod(x, m): 21 | assert 1 <= m 22 | z = inv_gcd(x, m) 23 | assert z[0] == 1 24 | return z[1] 25 | 26 | 27 | def crt(r, m): 28 | assert len(r) == len(m) 29 | n = len(r) 30 | r0 = 0 31 | m0 = 1 32 | for i in range(n): 33 | assert 1 <= m[i] 34 | r1 = r[i] % m[i] 35 | m1 = m[i] 36 | if m0 < m1: 37 | r0, r1 = r1, r0 38 | m0, m1 = m1, m0 39 | if m0 % m1 == 0: 40 | if r0 % m1 != r1: 41 | return (0, 0) 42 | continue 43 | g, im = inv_gcd(m0, m1) 44 | u1 = m1 // g 45 | if (r1 - r0) % g: 46 | return (0, 0) 47 | x = (r1 - r0) // g % u1 * im % u1 48 | r0 += x * m0 49 | m0 *= u1 50 | if r0 < 0: 51 | r0 += m0 52 | return (r0, m0) 53 | 54 | 55 | def floor_sum(n, m, a, b): 56 | ans = 0 57 | while True: 58 | if a < 0 or a >= m: 59 | k = a // m 60 | a = a % m 61 | if a < 0: 62 | a += m 63 | k -= 1 64 | ans += k * n * (n - 1) // 2 65 | if b < 0 or b >= m: 66 | k = b // m 67 | b = b % m 68 | if b < 0: 69 | b += m 70 | k -= 1 71 | ans += k * n 72 | y_max = (a * n + b) // m 73 | if y_max == 0: 74 | break 75 | x_max = y_max * m - b 76 | ans += (n - (x_max + a - 1) // a) * y_max 77 | n, m, a, b = y_max, a, m, (a - x_max % a) % a 78 | return ans 79 | -------------------------------------------------------------------------------- /dsu.py: -------------------------------------------------------------------------------- 1 | class dsu: 2 | n = 1 3 | parent_or_size = [-1 for i in range(n)] 4 | 5 | def __init__(self, N): 6 | self.n = N 7 | self.parent_or_size = [-1 for i in range(N)] 8 | 9 | def merge(self, a, b): 10 | assert 0 <= a < self.n, "0<=a 0: 50 | result2.append(result[i]) 51 | return result2 52 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Python Package 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | tags: [ 'v*' ] 7 | pull_request: 8 | branches: [ master, main ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build-and-test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9"] 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install build pytest pytest-cov 31 | 32 | - name: Build package 33 | run: python -m build 34 | 35 | - name: Test with pytest 36 | run: | 37 | pytest tests/ -v 38 | 39 | - name: Upload coverage reports to Codecov 40 | if: matrix.python-version == '3.11' 41 | uses: codecov/codecov-action@v3 42 | with: 43 | file: ./coverage.xml 44 | fail_ci_if_error: false 45 | 46 | publish: 47 | needs: build-and-test 48 | runs-on: ubuntu-latest 49 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') 50 | 51 | steps: 52 | - uses: actions/checkout@v4 53 | 54 | - name: Set up Python 55 | uses: actions/setup-python@v4 56 | with: 57 | python-version: '3.11' 58 | 59 | - name: Install dependencies 60 | run: | 61 | python -m pip install --upgrade pip 62 | pip install build twine 63 | 64 | - name: Build package 65 | run: python -m build 66 | 67 | - name: Publish to PyPI 68 | env: 69 | TWINE_USERNAME: __token__ 70 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 71 | run: | 72 | twine upload dist/* -------------------------------------------------------------------------------- /scc.py: -------------------------------------------------------------------------------- 1 | def scc(N, edges): 2 | M = len(edges) 3 | start = [0] * (N + 1) 4 | elist = [0] * M 5 | for e in edges: 6 | start[e[0] + 1] += 1 7 | for i in range(1, N + 1): 8 | start[i] += start[i - 1] 9 | counter = start[:] 10 | for e in edges: 11 | elist[counter[e[0]]] = e[1] 12 | counter[e[0]] += 1 13 | visited = [] 14 | low = [0] * N 15 | Ord = [-1] * N 16 | ids = [0] * N 17 | NG = [0, 0] 18 | 19 | def dfs(v): 20 | stack = [(v, -1, 0), (v, -1, 1)] 21 | while stack: 22 | v, bef, t = stack.pop() 23 | if t: 24 | if bef != -1 and Ord[v] != -1: 25 | low[bef] = min(low[bef], Ord[v]) 26 | stack.pop() 27 | continue 28 | low[v] = NG[0] 29 | Ord[v] = NG[0] 30 | NG[0] += 1 31 | visited.append(v) 32 | for i in range(start[v], start[v + 1]): 33 | to = elist[i] 34 | if Ord[to] == -1: 35 | stack.append((to, v, 0)) 36 | stack.append((to, v, 1)) 37 | else: 38 | low[v] = min(low[v], Ord[to]) 39 | else: 40 | if low[v] == Ord[v]: 41 | while True: 42 | u = visited.pop() 43 | Ord[u] = N 44 | ids[u] = NG[1] 45 | if u == v: 46 | break 47 | NG[1] += 1 48 | low[bef] = min(low[bef], low[v]) 49 | 50 | for i in range(N): 51 | if Ord[i] == -1: 52 | dfs(i) 53 | for i in range(N): 54 | ids[i] = NG[1] - 1 - ids[i] 55 | group_num = NG[1] 56 | counts = [0] * group_num 57 | for x in ids: 58 | counts[x] += 1 59 | groups = [[] for i in range(group_num)] 60 | for i in range(N): 61 | groups[ids[i]].append(i) 62 | return groups 63 | -------------------------------------------------------------------------------- /prime_fact.py: -------------------------------------------------------------------------------- 1 | import math 2 | import random 3 | import collections 4 | 5 | 6 | def is_probable_prime(n): 7 | if n < 2: 8 | return False 9 | SMALL_PRIME = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37) 10 | for p in SMALL_PRIME: 11 | if n == p: 12 | return True 13 | if n % p == 0: 14 | return False 15 | 16 | r = math.isqrt(n) 17 | if r * r == n: 18 | return False 19 | 20 | d = n - 1 21 | s = (d & -d).bit_length() - 1 22 | d >>= s 23 | 24 | for a in SMALL_PRIME: 25 | if a >= n: 26 | continue 27 | x = pow(a, d, n) 28 | if x == 1 or x == n - 1: 29 | continue 30 | for _ in range(s - 1): 31 | x = (x * x) % n 32 | if x == n - 1: 33 | break 34 | else: 35 | return False 36 | return True 37 | 38 | 39 | def _solve(N): 40 | if is_probable_prime(N): 41 | return N 42 | while True: 43 | x = random.randrange(N) 44 | c = random.randrange(N) 45 | y = (x * x + c) % N 46 | d = 1 47 | while d == 1: 48 | d = math.gcd(x - y, N) 49 | x = (x * x + c) % N 50 | y = (y * y + c) % N 51 | y = (y * y + c) % N 52 | if 1 < d < N: 53 | return _solve(d) 54 | 55 | 56 | def prime_fact(N): 57 | res = collections.Counter() 58 | p = 2 59 | while p <= 10**4 and N > 1: 60 | if N % p == 0: 61 | while N % p == 0: 62 | res[p] += 1 63 | N //= p 64 | p += 1 65 | while N > 1: 66 | p = _solve(N) 67 | while (N % p) == 0: 68 | res[p] += 1 69 | N //= p 70 | return res 71 | 72 | 73 | def divisors(N): 74 | PF = prime_fact(N) 75 | res = [1] 76 | for p, e in PF.items(): 77 | for i in range(len(res) * e): 78 | res.append(res[i] * p) 79 | return res 80 | 81 | 82 | def totient(N): 83 | PF = prime_fact(N) 84 | res = N 85 | for p in PF: 86 | res -= res // p 87 | return res 88 | 89 | 90 | def lcm(x, y): 91 | return (x * y) // math.gcd(x, y) 92 | -------------------------------------------------------------------------------- /two_sat.py: -------------------------------------------------------------------------------- 1 | def two_sat(n, clause): 2 | answer = [0] * n 3 | edges = [] 4 | N = 2 * n 5 | for s in clause: 6 | i, f, j, g = s 7 | edges.append((2 * i + (0 if f else 1), 2 * j + (1 if g else 0))) 8 | edges.append((2 * j + (0 if g else 1), 2 * i + (1 if f else 0))) 9 | M = len(edges) 10 | start = [0] * (N + 1) 11 | elist = [0] * M 12 | for e in edges: 13 | start[e[0] + 1] += 1 14 | for i in range(1, N + 1): 15 | start[i] += start[i - 1] 16 | counter = start[:] 17 | for e in edges: 18 | elist[counter[e[0]]] = e[1] 19 | counter[e[0]] += 1 20 | visited = [] 21 | low = [0] * N 22 | Ord = [-1] * N 23 | ids = [0] * N 24 | NG = [0, 0] 25 | 26 | def dfs(v): 27 | stack = [(v, -1, 0), (v, -1, 1)] 28 | while stack: 29 | v, bef, t = stack.pop() 30 | if t: 31 | if bef != -1 and Ord[v] != -1: 32 | low[bef] = min(low[bef], Ord[v]) 33 | stack.pop() 34 | continue 35 | low[v] = NG[0] 36 | Ord[v] = NG[0] 37 | NG[0] += 1 38 | visited.append(v) 39 | for i in range(start[v], start[v + 1]): 40 | to = elist[i] 41 | if Ord[to] == -1: 42 | stack.append((to, v, 0)) 43 | stack.append((to, v, 1)) 44 | else: 45 | low[v] = min(low[v], Ord[to]) 46 | else: 47 | if low[v] == Ord[v]: 48 | while True: 49 | u = visited.pop() 50 | Ord[u] = N 51 | ids[u] = NG[1] 52 | if u == v: 53 | break 54 | NG[1] += 1 55 | low[bef] = min(low[bef], low[v]) 56 | 57 | for i in range(N): 58 | if Ord[i] == -1: 59 | dfs(i) 60 | for i in range(N): 61 | ids[i] = NG[1] - 1 - ids[i] 62 | for i in range(n): 63 | if ids[2 * i] == ids[2 * i + 1]: 64 | return None 65 | answer[i] = ids[2 * i] < ids[2 * i + 1] 66 | return answer 67 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up Python 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: '3.11' 18 | 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install build twine 23 | 24 | - name: Test all modules 25 | run: | 26 | python -m py_compile *.py 27 | python -c " 28 | import sys 29 | import os 30 | sys.path.insert(0, os.getcwd()) 31 | 32 | modules = [ 33 | 'convolution', 'dsu', 'fenwicktree', 'fps', 'lazysegtree', 34 | 'acl_math', 'maxflow', 'mincostflow', 'prime_fact', 'scc', 35 | 'segtree', 'acl_string', 'two_sat' 36 | ] 37 | 38 | for module in modules: 39 | __import__(module) 40 | print(f'✓ {module}') 41 | 42 | print('All modules ready for release!') 43 | " 44 | 45 | - name: Create Release Archive 46 | run: | 47 | mkdir -p release 48 | cp *.py release/ 49 | cp README.md LICENSE release/ 50 | cd release 51 | tar -czf ../acl-python-${GITHUB_REF#refs/tags/}.tar.gz . 52 | 53 | - name: Create GitHub Release 54 | uses: softprops/action-gh-release@v1 55 | with: 56 | files: | 57 | acl-python-*.tar.gz 58 | body: | 59 | ## ACL for Python Release 60 | 61 | This release contains all the AC Library modules ported to Python. 62 | 63 | ### Files included: 64 | - All Python modules (.py files) 65 | - README.md with usage instructions 66 | - LICENSE file 67 | 68 | ### Compatibility: 69 | - Python 3.8+ 70 | - PyPy 3.9+ 71 | - Tested on AtCoder environment 72 | 73 | Download the tar.gz file and extract to use the library. 74 | draft: false 75 | prerelease: false 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=45", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "acl-python" 7 | version = "1.0.0" 8 | description = "AtCoder Library (ACL) ported to Python" 9 | readme = "README.md" 10 | requires-python = ">=3.8" 11 | license = {text = "Unlicense"} 12 | authors = [ 13 | {name = "shakayami", email = "shakayami@example.com"} 14 | ] 15 | keywords = ["competitive-programming", "algorithms", "data-structures", "atcoder"] 16 | classifiers = [ 17 | "Development Status :: 5 - Production/Stable", 18 | "Intended Audience :: Developers", 19 | "License :: Public Domain", 20 | "Programming Language :: Python :: 3", 21 | "Programming Language :: Python :: 3.8", 22 | "Programming Language :: Python :: 3.9", 23 | "Programming Language :: Python :: 3.10", 24 | "Programming Language :: Python :: 3.11", 25 | "Programming Language :: Python :: 3.12", 26 | "Programming Language :: Python :: Implementation :: CPython", 27 | "Programming Language :: Python :: Implementation :: PyPy", 28 | "Topic :: Software Development :: Libraries :: Python Modules", 29 | "Topic :: Scientific/Engineering :: Mathematics", 30 | ] 31 | 32 | [project.urls] 33 | Homepage = "https://github.com/shakayami/ACL-for-python" 34 | Repository = "https://github.com/shakayami/ACL-for-python" 35 | Issues = "https://github.com/shakayami/ACL-for-python/issues" 36 | Wiki = "https://github.com/shakayami/ACL-for-python/wiki" 37 | 38 | [tool.setuptools] 39 | py-modules = [ 40 | "convolution", 41 | "dsu", 42 | "fenwicktree", 43 | "fps", 44 | "lazysegtree", 45 | "acl_math", 46 | "maxflow", 47 | "mincostflow", 48 | "prime_fact", 49 | "scc", 50 | "segtree", 51 | "acl_string", 52 | "two_sat" 53 | ] 54 | 55 | [tool.black] 56 | line-length = 88 57 | target-version = ['py38'] 58 | include = '\.pyi?$' 59 | extend-exclude = ''' 60 | /( 61 | # directories 62 | __pycache__ 63 | | .git 64 | | .pytest_cache 65 | | build 66 | | dist 67 | )/ 68 | ''' 69 | 70 | [tool.flake8] 71 | max-line-length = 88 72 | extend-ignore = ["E203", "W503"] 73 | exclude = [ 74 | ".git", 75 | "__pycache__", 76 | "build", 77 | "dist", 78 | ".pytest_cache" 79 | ] -------------------------------------------------------------------------------- /tests/test_lazysegtree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import lazysegtree 10 | 11 | 12 | class TestLazySegTree(unittest.TestCase): 13 | """Test cases for lazysegtree module""" 14 | 15 | def practice2_k(self, N, Q, A, query, ans): 16 | """Practice problem 2-K implementation""" 17 | res = [] 18 | mod = 998244353 19 | 20 | def operate(a, b): 21 | return ((a[0] + b[0]) % mod, a[1] + b[1]) 22 | 23 | def mapping(f, x): 24 | return ((f[0] * x[0] + x[1] * f[1]) % mod, x[1]) 25 | 26 | def composition(f, g): 27 | return ((f[0] * g[0]) % mod, (g[1] * f[0] + f[1]) % mod) 28 | 29 | seg = lazysegtree.lazy_segtree( 30 | [(i, 1) for i in A], operate, (0, 0), mapping, composition, (1, 0) 31 | ) 32 | for i in range(Q): 33 | seq = query[i] 34 | if seq[0] == 0: 35 | dummy, l, r, b, c = seq 36 | seg.apply(l, r, (b, c)) 37 | else: 38 | dummy, l, r = seq 39 | res.append(seg.prod(l, r)[0]) 40 | self.assertEqual(res, ans) 41 | 42 | def test_practice2_k(self): 43 | """Test practice problem 2-K""" 44 | self.practice2_k( 45 | 5, 46 | 7, 47 | [1, 2, 3, 4, 5], 48 | [ 49 | (1, 0, 5), 50 | (0, 2, 4, 100, 101), 51 | (1, 0, 3), 52 | (0, 1, 3, 102, 103), 53 | (1, 2, 5), 54 | (0, 2, 5, 104, 105), 55 | (1, 0, 5), 56 | ], 57 | [15, 404, 41511, 4317767], 58 | ) 59 | 60 | def practice2_l(self, N, Q, A, query, ans): 61 | a = [(0, 1, 0) if _ == 0 else (0, 0, 1) for _ in A] 62 | 63 | def op(x, y): 64 | return (x[0] + y[0] + x[2] * y[1], x[1] + y[1], x[2] + y[2]) 65 | 66 | def mapping(f, x): 67 | if f == 0: 68 | return x 69 | else: 70 | return (x[1] * x[2] - x[0], x[2], x[1]) 71 | 72 | def composition(f, g): 73 | return f ^ g 74 | 75 | seg = lazysegtree.lazy_segtree(a, op, (0, 0, 0), mapping, composition, 0) 76 | res = [] 77 | for i in range(Q): 78 | t, l, r = query[i] 79 | l -= 1 80 | if t == 1: 81 | seg.apply(l, r, 1) 82 | else: 83 | tmp = seg.prod(l, r) 84 | res.append(tmp[0]) 85 | self.assertEqual(res, ans) 86 | 87 | def test_practice2_l(self): 88 | self.practice2_l( 89 | 5, 90 | 5, 91 | [0, 1, 0, 0, 1], 92 | [(2, 1, 5), (1, 3, 4), (2, 2, 5), (1, 1, 3), (2, 1, 2)], 93 | [2, 0, 1], 94 | ) 95 | 96 | 97 | if __name__ == "__main__": 98 | unittest.main() 99 | -------------------------------------------------------------------------------- /segtree.py: -------------------------------------------------------------------------------- 1 | class segtree: 2 | n = 1 3 | size = 1 4 | log = 2 5 | d = [0] 6 | op = None 7 | e = 10**15 8 | 9 | def __init__(self, V, OP, E): 10 | self.n = len(V) 11 | self.op = OP 12 | self.e = E 13 | self.log = (self.n - 1).bit_length() 14 | self.size = 1 << self.log 15 | self.d = [E for i in range(2 * self.size)] 16 | for i in range(self.n): 17 | self.d[self.size + i] = V[i] 18 | for i in range(self.size - 1, 0, -1): 19 | self.update(i) 20 | 21 | def set(self, p, x): 22 | assert 0 <= p and p < self.n 23 | p += self.size 24 | self.d[p] = x 25 | for i in range(1, self.log + 1): 26 | self.update(p >> i) 27 | 28 | def get(self, p): 29 | assert 0 <= p and p < self.n 30 | return self.d[p + self.size] 31 | 32 | def prod(self, l, r): 33 | assert 0 <= l and l <= r and r <= self.n 34 | sml = self.e 35 | smr = self.e 36 | l += self.size 37 | r += self.size 38 | while l < r: 39 | if l & 1: 40 | sml = self.op(sml, self.d[l]) 41 | l += 1 42 | if r & 1: 43 | smr = self.op(self.d[r - 1], smr) 44 | r -= 1 45 | l >>= 1 46 | r >>= 1 47 | return self.op(sml, smr) 48 | 49 | def all_prod(self): 50 | return self.d[1] 51 | 52 | def max_right(self, l, f): 53 | assert 0 <= l and l <= self.n 54 | assert f(self.e) 55 | if l == self.n: 56 | return self.n 57 | l += self.size 58 | sm = self.e 59 | while 1: 60 | while l % 2 == 0: 61 | l >>= 1 62 | if not (f(self.op(sm, self.d[l]))): 63 | while l < self.size: 64 | l = 2 * l 65 | if f(self.op(sm, self.d[l])): 66 | sm = self.op(sm, self.d[l]) 67 | l += 1 68 | return l - self.size 69 | sm = self.op(sm, self.d[l]) 70 | l += 1 71 | if (l & -l) == l: 72 | break 73 | return self.n 74 | 75 | def min_left(self, r, f): 76 | assert 0 <= r and r <= self.n 77 | assert f(self.e) 78 | if r == 0: 79 | return 0 80 | r += self.size 81 | sm = self.e 82 | while 1: 83 | r -= 1 84 | while r > 1 and (r % 2): 85 | r >>= 1 86 | if not (f(self.op(self.d[r], sm))): 87 | while r < self.size: 88 | r = 2 * r + 1 89 | if f(self.op(self.d[r], sm)): 90 | sm = self.op(self.d[r], sm) 91 | r -= 1 92 | return r + 1 - self.size 93 | sm = self.op(self.d[r], sm) 94 | if (r & -r) == r: 95 | break 96 | return 0 97 | 98 | def update(self, k): 99 | self.d[k] = self.op(self.d[2 * k], self.d[2 * k + 1]) 100 | 101 | def __str__(self): 102 | return str([self.get(i) for i in range(self.n)]) 103 | -------------------------------------------------------------------------------- /tests/test_basic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | 10 | class TestBasicImports(unittest.TestCase): 11 | """Basic import tests for all ACL modules""" 12 | 13 | def test_convolution_import(self): 14 | """Test convolution module import""" 15 | import convolution 16 | 17 | self.assertTrue(hasattr(convolution, "FFT")) 18 | 19 | def test_dsu_import(self): 20 | """Test DSU module import""" 21 | import dsu 22 | 23 | self.assertTrue(hasattr(dsu, "dsu")) 24 | 25 | def test_fenwicktree_import(self): 26 | """Test Fenwick Tree module import""" 27 | import fenwicktree 28 | 29 | self.assertTrue(hasattr(fenwicktree, "fenwick_tree")) 30 | 31 | def test_fps_import(self): 32 | """Test FPS module import""" 33 | import fps 34 | 35 | # Check for any common FPS functions or classes 36 | module_attrs = dir(fps) 37 | self.assertTrue(len(module_attrs) > 0) 38 | 39 | def test_lazysegtree_import(self): 40 | """Test Lazy Segment Tree module import""" 41 | import lazysegtree 42 | 43 | self.assertTrue(hasattr(lazysegtree, "lazy_segtree")) 44 | 45 | def test_math_import(self): 46 | """Test acl_math module import""" 47 | import acl_math 48 | 49 | # Check for common mathematical functions 50 | self.assertTrue( 51 | hasattr(acl_math, "inv_mod") 52 | or hasattr(acl_math, "crt") 53 | or hasattr(acl_math, "inv_gcd") 54 | ) 55 | 56 | def test_maxflow_import(self): 57 | """Test Max Flow module import""" 58 | import maxflow 59 | 60 | self.assertTrue(hasattr(maxflow, "mf_graph")) 61 | 62 | def test_mincostflow_import(self): 63 | """Test Min Cost Flow module import""" 64 | import mincostflow 65 | 66 | self.assertTrue(hasattr(mincostflow, "mcf_graph")) 67 | 68 | def test_prime_fact_import(self): 69 | """Test prime factorization module import""" 70 | import prime_fact 71 | 72 | # Check for common prime factorization functions 73 | self.assertTrue( 74 | hasattr(prime_fact, "prime_fact") 75 | or hasattr(prime_fact, "is_probable_prime") 76 | ) 77 | 78 | def test_scc_import(self): 79 | """Test SCC module import""" 80 | import scc 81 | 82 | self.assertTrue(hasattr(scc, "scc")) 83 | 84 | def test_segtree_import(self): 85 | """Test Segment Tree module import""" 86 | import segtree 87 | 88 | self.assertTrue(hasattr(segtree, "segtree")) 89 | 90 | def test_string_import(self): 91 | """Test acl_string module import""" 92 | import acl_string 93 | 94 | # Check for common string algorithms 95 | self.assertTrue(hasattr(acl_string, "string")) 96 | 97 | def test_two_sat_import(self): 98 | """Test 2-SAT module import""" 99 | import two_sat 100 | 101 | self.assertTrue(hasattr(two_sat, "two_sat")) 102 | 103 | 104 | if __name__ == "__main__": 105 | unittest.main() 106 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: [3.8, 3.9, '3.10', '3.11', '3.12'] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install pytest pytest-cov flake8 black 28 | 29 | - name: Lint with flake8 30 | run: | 31 | # stop the build if there are Python syntax errors or undefined names 32 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 33 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 34 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 35 | 36 | # - name: Check code formatting with black 37 | # run: | 38 | # black --check --diff . 39 | 40 | - name: Test Python compilation 41 | run: | 42 | python -m py_compile *.py 43 | 44 | - name: Run basic import tests 45 | run: | 46 | python -c " 47 | import sys 48 | import os 49 | sys.path.insert(0, os.getcwd()) 50 | 51 | modules = [ 52 | 'convolution', 'dsu', 'fenwicktree', 'fps', 'lazysegtree', 53 | 'acl_math', 'maxflow', 'mincostflow', 'prime_fact', 'scc', 54 | 'segtree', 'acl_string', 'two_sat' 55 | ] 56 | 57 | for module in modules: 58 | try: 59 | __import__(module) 60 | print(f'✓ {module} imported successfully') 61 | except ImportError as e: 62 | print(f'✗ Failed to import {module}: {e}') 63 | sys.exit(1) 64 | 65 | print('All modules imported successfully!') 66 | " 67 | 68 | pypy-test: 69 | runs-on: ubuntu-latest 70 | steps: 71 | - uses: actions/checkout@v4 72 | 73 | - name: Set up PyPy 74 | uses: actions/setup-python@v4 75 | with: 76 | python-version: 'pypy-3.9' 77 | 78 | - name: Test PyPy compilation 79 | run: | 80 | python -m py_compile *.py 81 | 82 | - name: Run PyPy import tests 83 | run: | 84 | python -c " 85 | import sys 86 | import os 87 | sys.path.insert(0, os.getcwd()) 88 | 89 | modules = [ 90 | 'convolution', 'dsu', 'fenwicktree', 'fps', 'lazysegtree', 91 | 'acl_math', 'maxflow', 'mincostflow', 'prime_fact', 'scc', 92 | 'segtree', 'acl_string', 'two_sat' 93 | ] 94 | 95 | for module in modules: 96 | try: 97 | __import__(module) 98 | print(f'✓ {module} imported successfully on PyPy') 99 | except ImportError as e: 100 | print(f'✗ Failed to import {module} on PyPy: {e}') 101 | sys.exit(1) 102 | 103 | print('All modules imported successfully on PyPy!') 104 | " -------------------------------------------------------------------------------- /tests/test_fps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import fps 10 | import random 11 | 12 | 13 | class TestFPS(unittest.TestCase): 14 | """Test cases for fps module""" 15 | 16 | def test_fps_basic(self): 17 | A = fps.FPS([1, 2, 3]) 18 | B = fps.FPS([4, 5, 6, 7]) 19 | self.assertEqual(A + B, fps.FPS([5, 7, 9, 7])) 20 | self.assertEqual(A - B, fps.FPS([998244350, 998244350, 998244350, 998244346])) 21 | self.assertEqual(A * B, fps.FPS([4, 13, 28, 34, 32, 21])) 22 | self.assertEqual( 23 | A / B, 24 | fps.FPS([748683265, 811073537, 857866241, 892960768, 928055297, 963149825]), 25 | ) 26 | self.assertEqual(A >> 1, fps.FPS([2, 3, 0])) 27 | self.assertEqual(A << 1, fps.FPS([0, 1, 2, 3])) 28 | A >>= 1 29 | self.assertEqual(A, fps.FPS([2, 3, 0])) 30 | A <<= 1 31 | self.assertEqual(A, fps.FPS([0, 2, 3, 0])) 32 | A = fps.FPS([1, 2, 3]) 33 | A += B 34 | self.assertEqual(A, fps.FPS([5, 7, 9, 7])) 35 | A = fps.FPS([1, 2, 3]) 36 | A -= B 37 | self.assertEqual(A, fps.FPS([998244350, 998244350, 998244350, 998244346])) 38 | A = fps.FPS([1, 2, 3]) 39 | A *= B 40 | self.assertEqual(A, fps.FPS([4, 13, 28, 34, 32, 21])) 41 | A = fps.FPS([1, 2, 3]) 42 | A /= B 43 | self.assertEqual( 44 | A, 45 | fps.FPS([748683265, 811073537, 857866241, 892960768, 928055297, 963149825]), 46 | ) 47 | A = fps.FPS([1, 2, 3]) 48 | self.assertEqual(A.diff(), fps.FPS([2, 6])) 49 | A = fps.FPS([1, 2, 3]) 50 | self.assertEqual(A.integral(), fps.FPS([0, 1, 1, 1])) 51 | 52 | def test_fps_log(self): 53 | A = fps.FPS([1, 1, 499122179, 166374064, 291154613]) 54 | self.assertEqual(A.log().resize(5), fps.FPS([0, 1, 2, 3, 4])) 55 | 56 | def test_fps_exp(self): 57 | A = fps.FPS([0, 1, 2, 3, 4]) 58 | self.assertEqual( 59 | A.exp().resize(5), fps.FPS([1, 1, 499122179, 166374064, 291154613]) 60 | ) 61 | 62 | def test_fps_exp_log(self): 63 | N = 10**4 64 | mod = 998244353 65 | seq = [0] + [random.randrange(mod) for i in range(N - 1)] 66 | A = fps.FPS(seq) 67 | B = A.exp() 68 | C = B.log() 69 | D = C.resize(N) 70 | self.assertEqual(A, D) 71 | 72 | def test_fps_log_exp(self): 73 | N = 10**4 74 | mod = 998244353 75 | seq = [1] + [random.randrange(mod) for i in range(N - 1)] 76 | A = fps.FPS(seq) 77 | B = A.log() 78 | C = B.exp() 79 | D = C.resize(N) 80 | self.assertEqual(A, D) 81 | 82 | def test_sqrt(self): 83 | N = 10**4 84 | mod = 998244353 85 | seq = [pow(random.randrange(1, mod), 2, mod)] + [ 86 | random.randrange(mod) for i in range(N - 1) 87 | ] 88 | A = fps.FPS(seq) 89 | B = A.sqrt() 90 | C = B * B 91 | D = C.resize(N) 92 | self.assertEqual(A, D) 93 | 94 | def test_fps_inverse(self): 95 | A = fps.FPS([5, 4, 3, 2, 1]) 96 | self.assertEqual( 97 | A.inv(), fps.FPS([598946612, 718735934, 862483121, 635682004, 163871793]) 98 | ) 99 | 100 | def test_fps_pow(self): 101 | A = fps.FPS([0, 0, 9, 12]) 102 | self.assertEqual(A.powfps(3), fps.FPS([0, 0, 0, 0])) 103 | A = fps.FPS([1, 1]) 104 | self.assertEqual(A.powfps(2), fps.FPS([1, 2])) 105 | A = fps.FPS([0, 0]) 106 | self.assertEqual(A.powfps(0), fps.FPS([1, 0])) 107 | 108 | 109 | if __name__ == "__main__": 110 | unittest.main() 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be added to the global gitignore or merged into this project gitignore. For a PyCharm 158 | # project, it is recommended to add these files to the global gitignore or project gitignore: 159 | # .idea/ 160 | 161 | # IDE 162 | .vscode/ 163 | .idea/ 164 | 165 | # OS 166 | .DS_Store 167 | Thumbs.db 168 | 169 | # Temporary files 170 | *.tmp 171 | *.temp 172 | *~ 173 | 174 | # Test results 175 | test-results/ 176 | .pytest_tmp/ -------------------------------------------------------------------------------- /tests/test_fenwicktree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import fenwicktree 10 | 11 | 12 | class TestFenwickTree(unittest.TestCase): 13 | """Test cases for fenwicktree module""" 14 | 15 | def practice2_b(self, N, Q, a, query, ans): 16 | FT = fenwicktree.fenwick_tree(N) 17 | for i in range(N): 18 | FT.add(i, a[i]) 19 | res = [] 20 | for t, a, b in query: 21 | if t == 0: 22 | FT.add(a, b) 23 | else: 24 | res.append(FT.sum(a, b)) 25 | self.assertEqual(res, ans) 26 | 27 | def test_sample(self): 28 | """Placeholder test - add your test cases here""" 29 | self.practice2_b( 30 | 5, 31 | 5, 32 | [1, 2, 3, 4, 5], 33 | [(1, 0, 5), (1, 2, 4), (0, 3, 10), (1, 0, 5), (1, 0, 3)], 34 | [15, 7, 25, 6], 35 | ) 36 | 37 | def test_fenwick_basic(self): 38 | """Test basic Fenwick Tree operations""" 39 | import fenwicktree 40 | 41 | ft = fenwicktree.fenwick_tree(5) 42 | ft.add(0, 1) 43 | ft.add(1, 2) 44 | ft.add(2, 3) 45 | self.assertEqual(ft.sum(0, 3), 6) 46 | self.assertEqual(ft.sum(1, 3), 5) 47 | 48 | def test_single_element(self): 49 | """Test Fenwick Tree with single element""" 50 | ft = fenwicktree.fenwick_tree(1) 51 | ft.add(0, 5) 52 | self.assertEqual(ft.sum(0, 1), 5) 53 | 54 | def test_empty_range(self): 55 | """Test empty range sum""" 56 | ft = fenwicktree.fenwick_tree(5) 57 | ft.add(0, 1) 58 | ft.add(1, 2) 59 | self.assertEqual(ft.sum(2, 2), 0) 60 | self.assertEqual(ft.sum(0, 0), 0) 61 | 62 | def test_negative_values(self): 63 | """Test Fenwick Tree with negative values""" 64 | ft = fenwicktree.fenwick_tree(5) 65 | ft.add(0, 5) 66 | ft.add(1, -3) 67 | ft.add(2, 7) 68 | ft.add(3, -2) 69 | 70 | self.assertEqual(ft.sum(0, 2), 2) # 5 + (-3) = 2 71 | self.assertEqual(ft.sum(1, 4), 2) # -3 + 7 + (-2) = 2 72 | self.assertEqual(ft.sum(0, 4), 7) # 5 + (-3) + 7 + (-2) = 7 73 | 74 | def test_multiple_additions(self): 75 | """Test multiple additions to same index""" 76 | ft = fenwicktree.fenwick_tree(3) 77 | ft.add(0, 1) 78 | ft.add(0, 2) 79 | ft.add(0, 3) 80 | self.assertEqual(ft.sum(0, 1), 6) 81 | 82 | ft.add(1, 5) 83 | ft.add(1, -2) 84 | self.assertEqual(ft.sum(0, 2), 9) # 6 + 3 = 9 85 | 86 | def test_range_sums(self): 87 | """Test various range sum queries""" 88 | ft = fenwicktree.fenwick_tree(6) 89 | values = [1, 3, 5, 7, 9, 11] 90 | for i, val in enumerate(values): 91 | ft.add(i, val) 92 | 93 | # Test prefix sums 94 | self.assertEqual(ft.sum(0, 1), 1) 95 | self.assertEqual(ft.sum(0, 2), 4) 96 | self.assertEqual(ft.sum(0, 3), 9) 97 | self.assertEqual(ft.sum(0, 6), 36) 98 | 99 | # Test range sums 100 | self.assertEqual(ft.sum(1, 3), 8) # 3 + 5 = 8 101 | self.assertEqual(ft.sum(2, 5), 21) # 5 + 7 + 9 = 21 102 | self.assertEqual(ft.sum(3, 6), 27) # 7 + 9 + 11 = 27 103 | 104 | def test_large_fenwick_tree(self): 105 | """Test Fenwick Tree with larger size""" 106 | n = 1000 107 | ft = fenwicktree.fenwick_tree(n) 108 | 109 | # Add values 1, 2, 3, ..., n 110 | for i in range(n): 111 | ft.add(i, i + 1) 112 | 113 | # Test some range sums 114 | self.assertEqual(ft.sum(0, 10), 55) # sum of 1..10 115 | self.assertEqual(ft.sum(0, 100), 5050) # sum of 1..100 116 | self.assertEqual(ft.sum(50, 100), 3775) # sum of 51..100 117 | 118 | def test_zero_initialization(self): 119 | """Test that Fenwick Tree starts with all zeros""" 120 | ft = fenwicktree.fenwick_tree(5) 121 | for i in range(6): 122 | self.assertEqual(ft.sum(0, i), 0) 123 | 124 | def test_boundary_conditions(self): 125 | """Test boundary conditions""" 126 | ft = fenwicktree.fenwick_tree(5) 127 | ft.add(0, 1) 128 | ft.add(4, 5) 129 | 130 | self.assertEqual(ft.sum(0, 1), 1) 131 | self.assertEqual(ft.sum(4, 5), 5) 132 | self.assertEqual(ft.sum(0, 5), 6) 133 | self.assertEqual(ft.sum(1, 4), 0) 134 | 135 | 136 | if __name__ == "__main__": 137 | unittest.main() 138 | -------------------------------------------------------------------------------- /mincostflow.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | 3 | 4 | class mcf_graph: 5 | n = 1 6 | pos = [] 7 | g = [[]] 8 | 9 | def __init__(self, N): 10 | self.n = N 11 | self.pos = [] 12 | self.g = [[] for i in range(N)] 13 | 14 | def add_edge(self, From, To, cap, cost): 15 | assert 0 <= From and From < self.n 16 | assert 0 <= To and To < self.n 17 | m = len(self.pos) 18 | self.pos.append((From, len(self.g[From]))) 19 | self.g[From].append( 20 | {"to": To, "rev": len(self.g[To]), "cap": cap, "cost": cost} 21 | ) 22 | self.g[To].append( 23 | {"to": From, "rev": len(self.g[From]) - 1, "cap": 0, "cost": -cost} 24 | ) 25 | 26 | def get_edge(self, i): 27 | m = len(self.pos) 28 | assert 0 <= i and i < m 29 | _e = self.g[self.pos[i][0]][self.pos[i][1]] 30 | _re = self.g[_e["to"]][_e["rev"]] 31 | return { 32 | "from": self.pos[i][0], 33 | "to": _e["to"], 34 | "cap": _e["cap"] + _re["cap"], 35 | "flow": _re["cap"], 36 | "cost": _e["cost"], 37 | } 38 | 39 | def edges(self): 40 | m = len(self.pos) 41 | result = [{} for i in range(m)] 42 | for i in range(m): 43 | tmp = self.get_edge(i) 44 | result[i]["from"] = tmp["from"] 45 | result[i]["to"] = tmp["to"] 46 | result[i]["cap"] = tmp["cap"] 47 | result[i]["flow"] = tmp["flow"] 48 | result[i]["cost"] = tmp["cost"] 49 | return result 50 | 51 | def flow(self, s, t, flow_limit=-1 - (-1 << 63)): 52 | return self.slope(s, t, flow_limit)[-1] 53 | 54 | def slope(self, s, t, flow_limit=-1 - (-1 << 63)): 55 | assert 0 <= s and s < self.n 56 | assert 0 <= t and t < self.n 57 | assert s != t 58 | """ 59 | variants (C = maxcost): 60 | -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0 61 | reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge 62 | """ 63 | dual = [0 for i in range(self.n)] 64 | dist = [0 for i in range(self.n)] 65 | pv = [0 for i in range(self.n)] 66 | pe = [0 for i in range(self.n)] 67 | vis = [False for i in range(self.n)] 68 | 69 | def dual_ref(): 70 | for i in range(self.n): 71 | dist[i] = -1 - (-1 << 63) 72 | pv[i] = -1 73 | pe[i] = -1 74 | vis[i] = False 75 | que = [] 76 | heapq.heappush(que, (0, s)) 77 | dist[s] = 0 78 | while que: 79 | v = heapq.heappop(que)[1] 80 | if vis[v]: 81 | continue 82 | vis[v] = True 83 | if v == t: 84 | break 85 | """ 86 | dist[v] = shortest(s, v) + dual[s] - dual[v] 87 | dist[v] >= 0 (all reduced cost are positive) 88 | dist[v] <= (n-1)C 89 | """ 90 | for i in range(len(self.g[v])): 91 | e = self.g[v][i] 92 | if vis[e["to"]] or (not (e["cap"])): 93 | continue 94 | """ 95 | |-dual[e.to]+dual[v]| <= (n-1)C 96 | cost <= C - -(n-1)C + 0 = nC 97 | """ 98 | cost = e["cost"] - dual[e["to"]] + dual[v] 99 | if dist[e["to"]] - dist[v] > cost: 100 | dist[e["to"]] = dist[v] + cost 101 | pv[e["to"]] = v 102 | pe[e["to"]] = i 103 | heapq.heappush(que, (dist[e["to"]], e["to"])) 104 | if not (vis[t]): 105 | return False 106 | for v in range(self.n): 107 | if not (vis[v]): 108 | continue 109 | dual[v] -= dist[t] - dist[v] 110 | return True 111 | 112 | flow = 0 113 | cost = 0 114 | prev_cost = -1 115 | result = [(flow, cost)] 116 | while flow < flow_limit: 117 | if not (dual_ref()): 118 | break 119 | c = flow_limit - flow 120 | v = t 121 | while v != s: 122 | c = min(c, self.g[pv[v]][pe[v]]["cap"]) 123 | v = pv[v] 124 | v = t 125 | while v != s: 126 | self.g[pv[v]][pe[v]]["cap"] -= c 127 | self.g[v][self.g[pv[v]][pe[v]]["rev"]]["cap"] += c 128 | v = pv[v] 129 | d = -dual[s] 130 | flow += c 131 | cost += c * d 132 | if prev_cost == d: 133 | result.pop() 134 | result.append((flow, cost)) 135 | prev_cost = cost 136 | return result 137 | -------------------------------------------------------------------------------- /maxflow.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | class mf_graph: 5 | n = 0 6 | g = [] 7 | 8 | def __init__(self, n_): 9 | self.n = n_ 10 | self.g = [[] for i in range(self.n)] 11 | self.pos = [] 12 | 13 | class _edge: 14 | to = 0 15 | rev = 0 16 | cap = 0 17 | 18 | def __init__(self, to_, rev_, cap_): 19 | self.to = to_ 20 | self.rev = rev_ 21 | self.cap = cap_ 22 | 23 | class edge: 24 | From = 0 25 | To = 0 26 | Cap = 0 27 | Flow = 0 28 | 29 | def __init__(self, from_, to_, cap_, flow_): 30 | self.From = from_ 31 | self.To = to_ 32 | self.Cap = cap_ 33 | self.Flow = flow_ 34 | 35 | def add_edge(self, From_, To_, Cap_): 36 | assert 0 <= From_ and From_ < self.n 37 | assert 0 <= To_ and To_ < self.n 38 | assert 0 <= Cap_ 39 | m = len(self.pos) 40 | self.pos.append((From_, len(self.g[From_]))) 41 | from_id = len(self.g[From_]) 42 | to_id = len(self.g[To_]) 43 | if From_ == To_: 44 | to_id += 1 45 | self.g[From_].append(self._edge(To_, to_id, Cap_)) 46 | self.g[To_].append(self._edge(From_, from_id, 0)) 47 | return m 48 | 49 | def get_edge(self, i): 50 | m = len(self.pos) 51 | assert 0 <= i and i < m 52 | _e = self.g[self.pos[i][0]][self.pos[i][1]] 53 | _re = self.g[_e.to][_e.rev] 54 | return self.edge(self.pos[i][0], _e.to, _e.cap + _re.cap, _re.cap) 55 | 56 | def edges(self, isdict=True): 57 | m = len(self.pos) 58 | result = [] 59 | for i in range(m): 60 | if isdict: 61 | e = self.get_edge(i) 62 | result.append( 63 | {"from": e.From, "to": e.To, "cap": e.Cap, "flow": e.Flow} 64 | ) 65 | else: 66 | result.append(self.get_edge(i)) 67 | return result 68 | 69 | def change_edge(self, i, new_cap, new_flow): 70 | m = len(self.pos) 71 | assert 0 <= i and i < m 72 | assert 0 <= new_flow and new_flow <= new_cap 73 | _e = self.g[self.pos[i][0]][self.pos[i][1]] 74 | _re = self.g[_e.to][_e.rev] 75 | _e.cap = new_cap - new_flow 76 | _re.cap = new_flow 77 | assert id(_e) == id(self.g[self.pos[i][0]][self.pos[i][1]]) 78 | assert id(_re) == id(self.g[_e.to][_e.rev]) 79 | 80 | def flow(self, s, t, flow_limit=(1 << 63) - 1): 81 | assert 0 <= s and s < self.n 82 | assert 0 <= t and t < self.n 83 | assert s != t 84 | level = [0 for i in range(self.n)] 85 | Iter = [0 for i in range(self.n)] 86 | que = collections.deque([]) 87 | 88 | def bfs(): 89 | for i in range(self.n): 90 | level[i] = -1 91 | level[s] = 0 92 | que.clear() 93 | que.append(s) 94 | while que: 95 | v = que.popleft() 96 | for e in self.g[v]: 97 | if e.cap == 0 or level[e.to] >= 0: 98 | continue 99 | level[e.to] = level[v] + 1 100 | if e.to == t: 101 | return 102 | que.append(e.to) 103 | 104 | def dfs(v, up): 105 | if v == s: 106 | return up 107 | res = 0 108 | level_v = level[v] 109 | for i in range(Iter[v], len(self.g[v])): 110 | e = self.g[v][i] 111 | assert id(e) == id(self.g[v][i]) 112 | if level_v <= level[e.to] or self.g[e.to][e.rev].cap == 0: 113 | continue 114 | d = dfs(e.to, min(up - res, self.g[e.to][e.rev].cap)) 115 | if d <= 0: 116 | continue 117 | self.g[v][i].cap += d 118 | self.g[e.to][e.rev].cap -= d 119 | res += d 120 | if res == up: 121 | return res 122 | level[v] = self.n 123 | return res 124 | 125 | flow = 0 126 | while flow < flow_limit: 127 | bfs() 128 | if level[t] == -1: 129 | break 130 | Iter = [0 for i in range(self.n)] 131 | f = dfs(t, flow_limit - flow) 132 | if not (f): 133 | break 134 | flow += f 135 | return flow 136 | 137 | def min_cut(self, s): 138 | visited = [False for i in range(self.n)] 139 | que = collections.deque([s]) 140 | while que: 141 | p = que.popleft() 142 | visited[p] = True 143 | for e in self.g[p]: 144 | if e.cap and not (visited[e.to]): 145 | visited[e.to] = True 146 | que.append(e.to) 147 | return visited 148 | -------------------------------------------------------------------------------- /tests/test_two_sat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import two_sat 10 | 11 | 12 | class TestTwoSAT(unittest.TestCase): 13 | """Test cases for two_sat module""" 14 | 15 | def test_two_sat_basic(self): 16 | """Test basic 2-SAT operations""" 17 | # Basic test: (x0 OR x1) AND (NOT x0 OR x1) AND (x0 OR NOT x1) 18 | # This should be satisfiable with x0=True, x1=True 19 | clauses = [ 20 | (0, True, 1, True), # x0 OR x1 21 | (0, False, 1, True), # NOT x0 OR x1 22 | (0, True, 1, False), # x0 OR NOT x1 23 | ] 24 | result = two_sat.two_sat(2, clauses) 25 | self.assertIsNotNone(result) 26 | self.assertEqual(len(result), 2) 27 | 28 | # Verify the solution satisfies all clauses 29 | x0, x1 = result[0], result[1] 30 | self.assertTrue(x0 or x1) # x0 OR x1 31 | self.assertTrue(not x0 or x1) # NOT x0 OR x1 32 | self.assertTrue(x0 or not x1) # x0 OR NOT x1 33 | 34 | def test_two_sat_satisfiable(self): 35 | """Test satisfiable 2-SAT instances""" 36 | # Test case 1: Simple satisfiable case 37 | # (x0 OR x1) AND (NOT x0 OR NOT x1) 38 | clauses = [ 39 | (0, True, 1, True), # x0 OR x1 40 | (0, False, 1, False), # NOT x0 OR NOT x1 41 | ] 42 | result = two_sat.two_sat(2, clauses) 43 | self.assertIsNotNone(result) 44 | 45 | # Verify solution 46 | x0, x1 = result[0], result[1] 47 | self.assertTrue(x0 or x1) 48 | self.assertTrue(not x0 or not x1) 49 | 50 | # Test case 2: More complex satisfiable case 51 | # (x0 OR x1) AND (x1 OR x2) AND (NOT x0 OR NOT x2) 52 | clauses = [ 53 | (0, True, 1, True), # x0 OR x1 54 | (1, True, 2, True), # x1 OR x2 55 | (0, False, 2, False), # NOT x0 OR NOT x2 56 | ] 57 | result = two_sat.two_sat(3, clauses) 58 | self.assertIsNotNone(result) 59 | 60 | # Verify solution 61 | x0, x1, x2 = result[0], result[1], result[2] 62 | self.assertTrue(x0 or x1) 63 | self.assertTrue(x1 or x2) 64 | self.assertTrue(not x0 or not x2) 65 | 66 | def test_two_sat_unsatisfiable(self): 67 | """Test unsatisfiable 2-SAT instances""" 68 | # Test case 1: Simple unsatisfiable case 69 | # (x0 OR x0) AND (NOT x0 OR NOT x0) - equivalent to x0 AND NOT x0 70 | clauses = [ 71 | (0, True, 0, True), # x0 OR x0 (equivalent to x0) 72 | (0, False, 0, False), # NOT x0 OR NOT x0 (equivalent to NOT x0) 73 | ] 74 | result = two_sat.two_sat(1, clauses) 75 | self.assertIsNone(result) 76 | 77 | # Test case 2: More complex unsatisfiable case 78 | # (x0 OR x1) AND (NOT x0 OR x1) AND (x0 OR NOT x1) AND (NOT x0 OR NOT x1) 79 | clauses = [ 80 | (0, True, 1, True), # x0 OR x1 81 | (0, False, 1, True), # NOT x0 OR x1 82 | (0, True, 1, False), # x0 OR NOT x1 83 | (0, False, 1, False), # NOT x0 OR NOT x1 84 | ] 85 | result = two_sat.two_sat(2, clauses) 86 | self.assertIsNone(result) 87 | 88 | # Test case 3: Direct contradiction - force x0 to be both True and False 89 | # x0 AND NOT x0 - this should be unsatisfiable 90 | clauses = [ 91 | (0, True, 0, True), # x0 (force x0 to be True) 92 | (0, False, 0, False), # NOT x0 (force x0 to be False) 93 | (1, True, 1, True), # x1 (dummy clause to have more variables) 94 | ] 95 | result = two_sat.two_sat(2, clauses) 96 | self.assertIsNone(result) 97 | 98 | def test_single_variable(self): 99 | """Test single variable cases""" 100 | # Test: x0 must be True 101 | clauses = [(0, True, 0, True)] # x0 OR x0 102 | result = two_sat.two_sat(1, clauses) 103 | self.assertIsNotNone(result) 104 | self.assertTrue(result[0]) 105 | 106 | # Test: x0 must be False 107 | clauses = [(0, False, 0, False)] # NOT x0 OR NOT x0 108 | result = two_sat.two_sat(1, clauses) 109 | self.assertIsNotNone(result) 110 | self.assertFalse(result[0]) 111 | 112 | def test_empty_clauses(self): 113 | """Test with no clauses (should be satisfiable)""" 114 | result = two_sat.two_sat(3, []) 115 | self.assertIsNotNone(result) 116 | self.assertEqual(len(result), 3) 117 | 118 | def test_multiple_solutions(self): 119 | """Test case with multiple valid solutions""" 120 | # Just x0 OR x1 - multiple solutions possible 121 | clauses = [(0, True, 1, True)] 122 | result = two_sat.two_sat(2, clauses) 123 | self.assertIsNotNone(result) 124 | 125 | # Verify the solution satisfies the constraint 126 | x0, x1 = result[0], result[1] 127 | self.assertTrue(x0 or x1) 128 | 129 | 130 | if __name__ == "__main__": 131 | unittest.main() 132 | -------------------------------------------------------------------------------- /acl_string.py: -------------------------------------------------------------------------------- 1 | class string: 2 | def sa_is(s, upper): 3 | n = len(s) 4 | if n == 0: 5 | return [] 6 | if n == 1: 7 | return [0] 8 | if n == 2: 9 | if s[0] < s[1]: 10 | return [0, 1] 11 | else: 12 | return [1, 0] 13 | sa = [0] * n 14 | ls = [0] * n 15 | for i in range(n - 2, -1, -1): 16 | ls[i] = ls[i + 1] if (s[i] == s[i + 1]) else (s[i] < s[i + 1]) 17 | sum_l = [0] * (upper + 1) 18 | sum_s = [0] * (upper + 1) 19 | for i in range(n): 20 | if not (ls[i]): 21 | sum_s[s[i]] += 1 22 | else: 23 | sum_l[s[i] + 1] += 1 24 | for i in range(upper + 1): 25 | sum_s[i] += sum_l[i] 26 | if i < upper: 27 | sum_l[i + 1] += sum_s[i] 28 | 29 | def induce(lms): 30 | for i in range(n): 31 | sa[i] = -1 32 | buf = sum_s[:] 33 | for d in lms: 34 | if d == n: 35 | continue 36 | sa[buf[s[d]]] = d 37 | buf[s[d]] += 1 38 | buf = sum_l[:] 39 | sa[buf[s[n - 1]]] = n - 1 40 | buf[s[n - 1]] += 1 41 | for i in range(n): 42 | v = sa[i] 43 | if v >= 1 and not (ls[v - 1]): 44 | sa[buf[s[v - 1]]] = v - 1 45 | buf[s[v - 1]] += 1 46 | buf = sum_l[:] 47 | for i in range(n - 1, -1, -1): 48 | v = sa[i] 49 | if v >= 1 and ls[v - 1]: 50 | buf[s[v - 1] + 1] -= 1 51 | sa[buf[s[v - 1] + 1]] = v - 1 52 | 53 | lms_map = [-1] * (n + 1) 54 | m = 0 55 | for i in range(1, n): 56 | if not (ls[i - 1]) and ls[i]: 57 | lms_map[i] = m 58 | m += 1 59 | lms = [] 60 | for i in range(1, n): 61 | if not (ls[i - 1]) and ls[i]: 62 | lms.append(i) 63 | induce(lms) 64 | if m: 65 | sorted_lms = [] 66 | for v in sa: 67 | if lms_map[v] != -1: 68 | sorted_lms.append(v) 69 | rec_s = [0] * m 70 | rec_upper = 0 71 | rec_s[lms_map[sorted_lms[0]]] = 0 72 | for i in range(1, m): 73 | l = sorted_lms[i - 1] 74 | r = sorted_lms[i] 75 | end_l = lms[lms_map[l] + 1] if (lms_map[l] + 1 < m) else n 76 | end_r = lms[lms_map[r] + 1] if (lms_map[r] + 1 < m) else n 77 | same = True 78 | if end_l - l != end_r - r: 79 | same = False 80 | else: 81 | while l < end_l: 82 | if s[l] != s[r]: 83 | break 84 | l += 1 85 | r += 1 86 | if (l == n) or (s[l] != s[r]): 87 | same = False 88 | if not (same): 89 | rec_upper += 1 90 | rec_s[lms_map[sorted_lms[i]]] = rec_upper 91 | rec_sa = string.sa_is(rec_s, rec_upper) 92 | for i in range(m): 93 | sorted_lms[i] = lms[rec_sa[i]] 94 | induce(sorted_lms) 95 | return sa 96 | 97 | def suffix_array_upper(s, upper): 98 | assert 0 <= upper 99 | for d in s: 100 | assert 0 <= d and d <= upper 101 | return string.sa_is(s, upper) 102 | 103 | def suffix_array(s): 104 | n = len(s) 105 | if type(s) == str: 106 | s2 = [ord(i) for i in s] 107 | return string.sa_is(s2, 255) 108 | else: 109 | idx = list(range(n)) 110 | idx.sort(key=lambda x: s[x]) 111 | s2 = [0] * n 112 | now = 0 113 | for i in range(n): 114 | if i and s[idx[i - 1]] != s[idx[i]]: 115 | now += 1 116 | s2[idx[i]] = now 117 | return string.sa_is(s2, now) 118 | 119 | def lcp_array(s, sa): 120 | n = len(s) 121 | assert n >= 1 122 | rnk = [0] * n 123 | for i in range(n): 124 | rnk[sa[i]] = i 125 | lcp = [0] * (n - 1) 126 | h = 0 127 | for i in range(n): 128 | if h > 0: 129 | h -= 1 130 | if rnk[i] == 0: 131 | continue 132 | j = sa[rnk[i] - 1] 133 | while j + h < n and i + h < n: 134 | if s[j + h] != s[i + h]: 135 | break 136 | h += 1 137 | lcp[rnk[i] - 1] = h 138 | return lcp 139 | 140 | def z_algorithm(s): 141 | n = len(s) 142 | if n == 0: 143 | return [] 144 | z = [0] * n 145 | i = 1 146 | j = 0 147 | while i < n: 148 | z[i] = 0 if (j + z[j] <= i) else min(j + z[j] - i, z[i - j]) 149 | while (i + z[i] < n) and (s[z[i]] == s[i + z[i]]): 150 | z[i] += 1 151 | if j + z[j] < i + z[i]: 152 | j = i 153 | i += 1 154 | z[0] = n 155 | return z 156 | -------------------------------------------------------------------------------- /lazysegtree.py: -------------------------------------------------------------------------------- 1 | class lazy_segtree: 2 | def update(self, k): 3 | self.d[k] = self.op(self.d[2 * k], self.d[2 * k + 1]) 4 | 5 | def all_apply(self, k, f): 6 | self.d[k] = self.mapping(f, self.d[k]) 7 | if k < self.size: 8 | self.lz[k] = self.composition(f, self.lz[k]) 9 | 10 | def push(self, k): 11 | self.all_apply(2 * k, self.lz[k]) 12 | self.all_apply(2 * k + 1, self.lz[k]) 13 | self.lz[k] = self.identity 14 | 15 | def __init__(self, V, OP, E, MAPPING, COMPOSITION, ID): 16 | self.n = len(V) 17 | self.log = (self.n - 1).bit_length() 18 | self.size = 1 << self.log 19 | self.d = [E for i in range(2 * self.size)] 20 | self.lz = [ID for i in range(self.size)] 21 | self.e = E 22 | self.op = OP 23 | self.mapping = MAPPING 24 | self.composition = COMPOSITION 25 | self.identity = ID 26 | for i in range(self.n): 27 | self.d[self.size + i] = V[i] 28 | for i in range(self.size - 1, 0, -1): 29 | self.update(i) 30 | 31 | def set(self, p, x): 32 | assert 0 <= p and p < self.n 33 | p += self.size 34 | for i in range(self.log, 0, -1): 35 | self.push(p >> i) 36 | self.d[p] = x 37 | for i in range(1, self.log + 1): 38 | self.update(p >> i) 39 | 40 | def get(self, p): 41 | assert 0 <= p and p < self.n 42 | p += self.size 43 | for i in range(self.log, 0, -1): 44 | self.push(p >> i) 45 | return self.d[p] 46 | 47 | def prod(self, l, r): 48 | assert 0 <= l and l <= r and r <= self.n 49 | if l == r: 50 | return self.e 51 | l += self.size 52 | r += self.size 53 | for i in range(self.log, 0, -1): 54 | if ((l >> i) << i) != l: 55 | self.push(l >> i) 56 | if ((r >> i) << i) != r: 57 | self.push(r >> i) 58 | sml, smr = self.e, self.e 59 | while l < r: 60 | if l & 1: 61 | sml = self.op(sml, self.d[l]) 62 | l += 1 63 | if r & 1: 64 | r -= 1 65 | smr = self.op(self.d[r], smr) 66 | l >>= 1 67 | r >>= 1 68 | return self.op(sml, smr) 69 | 70 | def all_prod(self): 71 | return self.d[1] 72 | 73 | def apply_point(self, p, f): 74 | assert 0 <= p and p < self.n 75 | p += self.size 76 | for i in range(self.log, 0, -1): 77 | self.push(p >> i) 78 | self.d[p] = self.mapping(f, self.d[p]) 79 | for i in range(1, self.log + 1): 80 | self.update(p >> i) 81 | 82 | def apply(self, l, r, f): 83 | assert 0 <= l and l <= r and r <= self.n 84 | if l == r: 85 | return 86 | l += self.size 87 | r += self.size 88 | for i in range(self.log, 0, -1): 89 | if ((l >> i) << i) != l: 90 | self.push(l >> i) 91 | if ((r >> i) << i) != r: 92 | self.push((r - 1) >> i) 93 | l2, r2 = l, r 94 | while l < r: 95 | if l & 1: 96 | self.all_apply(l, f) 97 | l += 1 98 | if r & 1: 99 | r -= 1 100 | self.all_apply(r, f) 101 | l >>= 1 102 | r >>= 1 103 | l, r = l2, r2 104 | for i in range(1, self.log + 1): 105 | if ((l >> i) << i) != l: 106 | self.update(l >> i) 107 | if ((r >> i) << i) != r: 108 | self.update((r - 1) >> i) 109 | 110 | def max_right(self, l, g): 111 | assert 0 <= l and l <= self.n 112 | assert g(self.e) 113 | if l == self.n: 114 | return self.n 115 | l += self.size 116 | for i in range(self.log, 0, -1): 117 | self.push(l >> i) 118 | sm = self.e 119 | while 1: 120 | while l % 2 == 0: 121 | l >>= 1 122 | if not (g(self.op(sm, self.d[l]))): 123 | while l < self.size: 124 | self.push(l) 125 | l = 2 * l 126 | if g(self.op(sm, self.d[l])): 127 | sm = self.op(sm, self.d[l]) 128 | l += 1 129 | return l - self.size 130 | sm = self.op(sm, self.d[l]) 131 | l += 1 132 | if (l & -l) == l: 133 | break 134 | return self.n 135 | 136 | def min_left(self, r, g): 137 | assert 0 <= r and r <= self.n 138 | assert g(self.e) 139 | if r == 0: 140 | return 0 141 | r += self.size 142 | for i in range(self.log, 0, -1): 143 | self.push((r - 1) >> i) 144 | sm = self.e 145 | while 1: 146 | r -= 1 147 | while r > 1 and (r % 2): 148 | r >>= 1 149 | if not (g(self.op(self.d[r], sm))): 150 | while r < self.size: 151 | self.push(r) 152 | r = 2 * r + 1 153 | if g(self.op(self.d[r], sm)): 154 | sm = self.op(self.d[r], sm) 155 | r -= 1 156 | return r + 1 - self.size 157 | sm = self.op(self.d[r], sm) 158 | if (r & -r) == r: 159 | break 160 | return 0 161 | -------------------------------------------------------------------------------- /tests/test_acl_math.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import acl_math 10 | 11 | 12 | class TestACLMath(unittest.TestCase): 13 | """Test cases for acl_math module""" 14 | 15 | def test_floorsum(self): 16 | query = [ 17 | (4, 10, 6, 3, 3), 18 | (6, 5, 4, 3, 13), 19 | (1, 1, 0, 0, 0), 20 | (31415, 92653, 58979, 32384, 314095480), 21 | (1000000000, 1000000000, 999999999, 999999999, 499999999500000000), 22 | ] 23 | for n, m, a, b, ans in query: 24 | self.assertEqual(acl_math.floor_sum(n, m, a, b), ans) 25 | 26 | def test_crt(self): 27 | A = 37 28 | B = 56 29 | C = 15 30 | S = set(range(A * B * C)) 31 | for a in range(A): 32 | for b in range(B): 33 | for c in range(C): 34 | x, X = acl_math.crt((a, b, c), (A, B, C)) 35 | self.assertEqual(X, A * B * C) 36 | self.assertTrue(x in S) 37 | S.discard(x) 38 | 39 | def test_inv_gcd_basic(self): 40 | """Test basic inv_gcd functionality""" 41 | # gcd(3, 7) = 1, 3 * 5 ≡ 1 (mod 7) 42 | g, x = acl_math.inv_gcd(3, 7) 43 | self.assertEqual(g, 1) 44 | self.assertEqual((3 * x) % 7, 1) 45 | 46 | # gcd(6, 9) = 3 47 | g, x = acl_math.inv_gcd(6, 9) 48 | self.assertEqual(g, 3) 49 | 50 | def test_inv_mod_basic(self): 51 | """Test basic modular inverse""" 52 | # 3 * 5 ≡ 1 (mod 7) 53 | self.assertEqual(acl_math.inv_mod(3, 7), 5) 54 | 55 | # 2 * 4 ≡ 1 (mod 7) 56 | self.assertEqual(acl_math.inv_mod(2, 7), 4) 57 | 58 | # Test with larger numbers 59 | inv = acl_math.inv_mod(123, 997) # 997 is prime 60 | self.assertEqual((123 * inv) % 997, 1) 61 | 62 | def test_inv_mod_edge_cases(self): 63 | """Test edge cases for modular inverse""" 64 | # Inverse of 1 is always 1 65 | self.assertEqual(acl_math.inv_mod(1, 5), 1) 66 | self.assertEqual(acl_math.inv_mod(1, 1000), 1) 67 | 68 | def test_crt_simple_cases(self): 69 | """Test simple CRT cases""" 70 | # x ≡ 2 (mod 3), x ≡ 3 (mod 5) 71 | # Solution: x ≡ 8 (mod 15) 72 | x, m = acl_math.crt([2, 3], [3, 5]) 73 | self.assertEqual(x, 8) 74 | self.assertEqual(m, 15) 75 | self.assertEqual(x % 3, 2) 76 | self.assertEqual(x % 5, 3) 77 | 78 | def test_crt_no_solution(self): 79 | """Test CRT with no solution""" 80 | # x ≡ 1 (mod 4), x ≡ 3 (mod 4) - no solution 81 | x, m = acl_math.crt([1, 3], [4, 4]) 82 | self.assertEqual((x, m), (0, 0)) 83 | 84 | def test_crt_single_constraint(self): 85 | """Test CRT with single constraint""" 86 | x, m = acl_math.crt([5], [7]) 87 | self.assertEqual(x, 5) 88 | self.assertEqual(m, 7) 89 | 90 | def test_crt_coprime_moduli(self): 91 | """Test CRT with coprime moduli""" 92 | # x ≡ 1 (mod 2), x ≡ 2 (mod 3), x ≡ 3 (mod 5) 93 | x, m = acl_math.crt([1, 2, 3], [2, 3, 5]) 94 | self.assertEqual(m, 30) # 2 * 3 * 5 95 | self.assertEqual(x % 2, 1) 96 | self.assertEqual(x % 3, 2) 97 | self.assertEqual(x % 5, 3) 98 | 99 | def test_floor_sum_edge_cases(self): 100 | """Test floor_sum edge cases""" 101 | # n = 0 should return 0 102 | self.assertEqual(acl_math.floor_sum(0, 5, 3, 2), 0) 103 | 104 | # m = 1 case 105 | result = acl_math.floor_sum(5, 1, 0, 0) 106 | self.assertEqual(result, 0) 107 | 108 | def test_floor_sum_negative_coefficients(self): 109 | """Test floor_sum with negative coefficients""" 110 | # Test that negative coefficients are handled properly 111 | result1 = acl_math.floor_sum(5, 7, -3, 2) 112 | self.assertIsInstance(result1, int) 113 | 114 | # Test with negative b 115 | result2 = acl_math.floor_sum(3, 5, 2, -1) 116 | self.assertIsInstance(result2, int) 117 | 118 | def test_floor_sum_large_values(self): 119 | """Test floor_sum with larger values""" 120 | result = acl_math.floor_sum(100, 97, 53, 29) 121 | # Verify by checking some properties 122 | self.assertIsInstance(result, int) 123 | self.assertGreaterEqual(result, 0) 124 | 125 | def test_inv_gcd_extended_cases(self): 126 | """Test extended cases for inv_gcd""" 127 | # Test with a = 0 128 | g, x = acl_math.inv_gcd(0, 5) 129 | self.assertEqual(g, 5) 130 | self.assertEqual(x, 0) 131 | 132 | # Test with larger numbers 133 | g, x = acl_math.inv_gcd(48, 18) 134 | self.assertEqual(g, 6) # gcd(48, 18) = 6 135 | 136 | def test_mathematical_properties(self): 137 | """Test mathematical properties of the functions""" 138 | # Test that (a * inv_mod(a, m)) % m == 1 for various coprime pairs 139 | test_cases = [(3, 7), (5, 11), (7, 13), (11, 17)] 140 | for a, m in test_cases: 141 | inv = acl_math.inv_mod(a, m) 142 | self.assertEqual((a * inv) % m, 1) 143 | 144 | # Test CRT property: if x ≡ r_i (mod m_i), then x % m_i == r_i 145 | r = [2, 3, 1] 146 | m = [5, 7, 11] 147 | x, _ = acl_math.crt(r, m) 148 | for i in range(len(r)): 149 | self.assertEqual(x % m[i], r[i]) 150 | 151 | 152 | if __name__ == "__main__": 153 | unittest.main() 154 | -------------------------------------------------------------------------------- /tests/test_mincostflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import mincostflow 10 | 11 | 12 | class TestMinCostFlow(unittest.TestCase): 13 | """Test cases for mincostflow module""" 14 | 15 | def practice2_e(self, n, k, A, ans): 16 | BIG = 10**9 17 | g = mincostflow.mcf_graph(2 * n + 2) 18 | s = 2 * n 19 | t = 2 * n + 1 20 | g.add_edge(s, t, n * k, BIG) 21 | for i in range(n): 22 | g.add_edge(s, i, k, 0) 23 | g.add_edge(n + i, t, k, 0) 24 | for i in range(n): 25 | for j in range(n): 26 | g.add_edge(i, n + j, 1, BIG - A[i][j]) 27 | result = g.flow(s, t, n * k) 28 | res = n * k * BIG - result[1] 29 | self.assertEqual(res, ans) 30 | 31 | def test_practice2_e_1(self): 32 | self.practice2_e(3, 1, [[5, 3, 2], [1, 4, 8], [7, 6, 9]], 19) 33 | 34 | def test_practice_e_2(self): 35 | self.practice2_e(3, 2, [[10, 10, 1], [10, 10, 1], [1, 1, 10]], 50) 36 | 37 | def test_simple_flow(self): 38 | """Test simple min cost flow""" 39 | g = mincostflow.mcf_graph(3) 40 | g.add_edge(0, 1, 5, 2) # capacity 5, cost 2 41 | g.add_edge(1, 2, 3, 1) # capacity 3, cost 1 42 | 43 | flow, cost = g.flow(0, 2, 3) 44 | self.assertEqual(flow, 3) 45 | self.assertEqual(cost, 9) # 3 * (2 + 1) = 9 46 | 47 | def test_no_path(self): 48 | """Test when there's no path""" 49 | g = mincostflow.mcf_graph(3) 50 | g.add_edge(0, 1, 5, 2) 51 | # No path from 0 to 2 52 | flow, cost = g.flow(0, 2, 10) 53 | self.assertEqual(flow, 0) 54 | self.assertEqual(cost, 0) 55 | 56 | def test_zero_flow_demand(self): 57 | """Test with zero flow demand""" 58 | g = mincostflow.mcf_graph(3) 59 | g.add_edge(0, 1, 5, 2) 60 | g.add_edge(1, 2, 3, 1) 61 | 62 | flow, cost = g.flow(0, 2, 0) 63 | self.assertEqual(flow, 0) 64 | self.assertEqual(cost, 0) 65 | 66 | def test_capacity_limit(self): 67 | """Test flow limited by capacity""" 68 | g = mincostflow.mcf_graph(3) 69 | g.add_edge(0, 1, 2, 1) # bottleneck capacity 2 70 | g.add_edge(1, 2, 10, 1) 71 | 72 | flow, cost = g.flow(0, 2, 5) # demand 5 but capacity allows only 2 73 | self.assertEqual(flow, 2) 74 | self.assertEqual(cost, 4) # 2 * (1 + 1) = 4 75 | 76 | def test_multiple_paths_different_costs(self): 77 | """Test multiple paths with different costs""" 78 | g = mincostflow.mcf_graph(4) 79 | # Path 1: 0 -> 1 -> 3 (cost = 1 + 1 = 2) 80 | g.add_edge(0, 1, 3, 1) 81 | g.add_edge(1, 3, 3, 1) 82 | # Path 2: 0 -> 2 -> 3 (cost = 2 + 3 = 5) 83 | g.add_edge(0, 2, 3, 2) 84 | g.add_edge(2, 3, 3, 3) 85 | 86 | # Should use cheaper path first 87 | flow, cost = g.flow(0, 3, 4) 88 | self.assertEqual(flow, 4) 89 | # First 3 units use path 1 (cost 6), next 1 unit uses path 2 (cost 5) 90 | self.assertEqual(cost, 11) 91 | 92 | def test_negative_cost_edges(self): 93 | """Test edges with negative costs""" 94 | g = mincostflow.mcf_graph(3) 95 | g.add_edge(0, 1, 5, -2) # negative cost 96 | g.add_edge(1, 2, 5, 1) 97 | 98 | flow, cost = g.flow(0, 2, 3) 99 | self.assertEqual(flow, 3) 100 | self.assertEqual(cost, -3) # 3 * (-2 + 1) = -3 101 | 102 | def test_edge_operations(self): 103 | """Test edge addition and retrieval""" 104 | g = mincostflow.mcf_graph(3) 105 | g.add_edge(0, 1, 5, 3) 106 | 107 | # Get edge (add_edge doesn't return edge id in this implementation) 108 | edge = g.get_edge(0) # First edge has index 0 109 | self.assertEqual(edge["from"], 0) 110 | self.assertEqual(edge["to"], 1) 111 | self.assertEqual(edge["cap"], 5) 112 | self.assertEqual(edge["flow"], 0) 113 | self.assertEqual(edge["cost"], 3) 114 | 115 | def test_edges_after_flow(self): 116 | """Test edge states after running min cost flow""" 117 | g = mincostflow.mcf_graph(3) 118 | g.add_edge(0, 1, 10, 2) # First edge 119 | g.add_edge(1, 2, 5, 1) # Second edge 120 | 121 | flow, cost = g.flow(0, 2, 5) 122 | self.assertEqual(flow, 5) 123 | 124 | # Check edge flow 125 | edge = g.get_edge(0) # First edge 126 | self.assertEqual(edge["flow"], 5) 127 | 128 | def test_all_edges(self): 129 | """Test getting all edges""" 130 | g = mincostflow.mcf_graph(3) 131 | g.add_edge(0, 1, 5, 2) 132 | g.add_edge(1, 2, 3, 1) 133 | 134 | edges = g.edges() 135 | self.assertEqual(len(edges), 2) 136 | 137 | self.assertEqual(edges[0]["from"], 0) 138 | self.assertEqual(edges[0]["to"], 1) 139 | self.assertEqual(edges[1]["from"], 1) 140 | self.assertEqual(edges[1]["to"], 2) 141 | 142 | def test_complex_network(self): 143 | """Test complex min cost flow network""" 144 | g = mincostflow.mcf_graph(5) 145 | g.add_edge(0, 1, 3, 1) 146 | g.add_edge(0, 2, 2, 2) 147 | g.add_edge(1, 2, 1, 1) 148 | g.add_edge(1, 3, 2, 2) 149 | g.add_edge(2, 3, 1, 1) 150 | g.add_edge(2, 4, 2, 3) 151 | g.add_edge(3, 4, 3, 1) 152 | 153 | flow, cost = g.flow(0, 4, 4) 154 | self.assertGreaterEqual(flow, 0) 155 | self.assertGreaterEqual(cost, 0) 156 | self.assertIsInstance(flow, int) 157 | self.assertIsInstance(cost, int) 158 | 159 | def test_self_loop_with_cost(self): 160 | """Test self-loop with cost (should not affect flow)""" 161 | g = mincostflow.mcf_graph(3) 162 | g.add_edge(0, 0, 10, 5) # Self-loop 163 | g.add_edge(0, 1, 5, 2) 164 | g.add_edge(1, 2, 5, 1) 165 | 166 | flow, cost = g.flow(0, 2, 3) 167 | self.assertEqual(flow, 3) 168 | self.assertEqual(cost, 9) # Should ignore self-loop 169 | 170 | def test_zero_cost_edges(self): 171 | """Test edges with zero cost""" 172 | g = mincostflow.mcf_graph(3) 173 | g.add_edge(0, 1, 5, 0) # zero cost 174 | g.add_edge(1, 2, 5, 0) # zero cost 175 | 176 | flow, cost = g.flow(0, 2, 3) 177 | self.assertEqual(flow, 3) 178 | self.assertEqual(cost, 0) 179 | 180 | 181 | if __name__ == "__main__": 182 | unittest.main() 183 | -------------------------------------------------------------------------------- /tests/test_maxflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import maxflow 10 | 11 | 12 | class TestMaxFlow(unittest.TestCase): 13 | """Test cases for maxflow module""" 14 | 15 | def tessoku_book_A68(self, N, M, query, ans): 16 | self.assertEqual(len(query), M) 17 | G = maxflow.mf_graph(N) 18 | for a, b, c in query: 19 | a -= 1 20 | b -= 1 21 | G.add_edge(a, b, c) 22 | 23 | res = G.flow(0, N - 1) 24 | self.assertEqual(ans, res) 25 | 26 | def test_tessoku_book_A68(self): 27 | self.tessoku_book_A68( 28 | 6, 29 | 7, 30 | [ 31 | (1, 2, 5), 32 | (1, 4, 4), 33 | (2, 3, 4), 34 | (2, 5, 7), 35 | (3, 6, 3), 36 | (4, 5, 3), 37 | (5, 6, 5), 38 | ], 39 | 8, 40 | ) 41 | 42 | def practice2_d(self, N, M, S, ans): 43 | S = [list(s) for s in S] 44 | G = maxflow.mf_graph(N * M + 2) 45 | for i in range(N): 46 | for j in range(M): 47 | if S[i][j] == "#": 48 | continue 49 | v = i * M + j 50 | if (i + j) % 2 == 0: 51 | G.add_edge(N * M, v, 1) 52 | else: 53 | G.add_edge(v, N * M + 1, 1) 54 | for i in range(N): 55 | for j in range(M): 56 | if (i + j) % 2 or S[i][j] == "#": 57 | continue 58 | v0 = i * M + j 59 | if i > 0 and S[i - 1][j] == ".": 60 | v1 = (i - 1) * M + j 61 | G.add_edge(v0, v1, 1) 62 | if j > 0 and S[i][j - 1] == ".": 63 | v1 = i * M + (j - 1) 64 | G.add_edge(v0, v1, 1) 65 | if i + 1 < N and S[i + 1][j] == ".": 66 | v1 = (i + 1) * M + j 67 | G.add_edge(v0, v1, 1) 68 | if j + 1 < M and S[i][j + 1] == ".": 69 | v1 = i * M + (j + 1) 70 | G.add_edge(v0, v1, 1) 71 | 72 | res = G.flow(N * M, N * M + 1) 73 | self.assertEqual(ans, res) 74 | 75 | def test_practice2_d(self): 76 | self.practice2_d(3, 3, ["#..", "..#", "..."], 3) 77 | 78 | def test_simple_flow(self): 79 | """Test simple max flow""" 80 | # Simple path: 0 -> 1 -> 2 81 | g = maxflow.mf_graph(3) 82 | g.add_edge(0, 1, 10) 83 | g.add_edge(1, 2, 5) 84 | flow = g.flow(0, 2) 85 | self.assertEqual(flow, 5) 86 | 87 | def test_no_path(self): 88 | """Test when there's no path from source to sink""" 89 | g = maxflow.mf_graph(3) 90 | g.add_edge(0, 1, 10) 91 | # No edge from 1 to 2 92 | flow = g.flow(0, 2) 93 | self.assertEqual(flow, 0) 94 | 95 | def test_single_node(self): 96 | """Test max flow with source same as sink (should be invalid)""" 97 | g = maxflow.mf_graph(2) 98 | g.add_edge(0, 1, 10) 99 | # Source and sink must be different in this implementation 100 | with self.assertRaises(AssertionError): 101 | g.flow(0, 0) 102 | 103 | def test_parallel_edges(self): 104 | """Test parallel edges between same vertices""" 105 | g = maxflow.mf_graph(2) 106 | g.add_edge(0, 1, 3) 107 | g.add_edge(0, 1, 5) 108 | g.add_edge(0, 1, 2) 109 | flow = g.flow(0, 1) 110 | self.assertEqual(flow, 10) # 3 + 5 + 2 111 | 112 | def test_multiple_paths(self): 113 | """Test multiple paths from source to sink""" 114 | # Diamond graph: 0 -> {1,2} -> 3 115 | g = maxflow.mf_graph(4) 116 | g.add_edge(0, 1, 10) 117 | g.add_edge(0, 2, 8) 118 | g.add_edge(1, 3, 6) 119 | g.add_edge(2, 3, 9) 120 | flow = g.flow(0, 3) 121 | self.assertEqual(flow, 14) # min(10,6) + min(8,8) = 6 + 8 122 | 123 | def test_bottleneck(self): 124 | """Test bottleneck in the middle""" 125 | # 0 -> 1 -> 2 -> 3 with bottleneck at 1->2 126 | g = maxflow.mf_graph(4) 127 | g.add_edge(0, 1, 100) 128 | g.add_edge(1, 2, 1) # Bottleneck 129 | g.add_edge(2, 3, 100) 130 | flow = g.flow(0, 3) 131 | self.assertEqual(flow, 1) 132 | 133 | def test_zero_capacity_edges(self): 134 | """Test edges with zero capacity""" 135 | g = maxflow.mf_graph(3) 136 | g.add_edge(0, 1, 0) 137 | g.add_edge(1, 2, 10) 138 | flow = g.flow(0, 2) 139 | self.assertEqual(flow, 0) 140 | 141 | def test_edge_operations(self): 142 | """Test edge addition and retrieval""" 143 | g = maxflow.mf_graph(3) 144 | edge_id = g.add_edge(0, 1, 5) 145 | 146 | # Get edge before flow 147 | edge = g.get_edge(edge_id) 148 | self.assertEqual(edge.From, 0) 149 | self.assertEqual(edge.To, 1) 150 | self.assertEqual(edge.Cap, 5) 151 | self.assertEqual(edge.Flow, 0) 152 | 153 | def test_edges_after_flow(self): 154 | """Test edge states after running max flow""" 155 | g = maxflow.mf_graph(3) 156 | edge_id = g.add_edge(0, 1, 10) 157 | g.add_edge(1, 2, 5) 158 | 159 | flow = g.flow(0, 2) 160 | self.assertEqual(flow, 5) 161 | 162 | # Check edge flow 163 | edge = g.get_edge(edge_id) 164 | self.assertEqual(edge.Flow, 5) 165 | 166 | def test_complex_network(self): 167 | """Test complex network with multiple sources and sinks""" 168 | # Create a more complex flow network 169 | g = maxflow.mf_graph(6) 170 | g.add_edge(0, 1, 16) 171 | g.add_edge(0, 2, 13) 172 | g.add_edge(1, 2, 10) 173 | g.add_edge(1, 3, 12) 174 | g.add_edge(2, 1, 4) 175 | g.add_edge(2, 4, 14) 176 | g.add_edge(3, 2, 9) 177 | g.add_edge(3, 5, 20) 178 | g.add_edge(4, 3, 7) 179 | g.add_edge(4, 5, 4) 180 | 181 | flow = g.flow(0, 5) 182 | self.assertGreater(flow, 0) 183 | self.assertIsInstance(flow, int) 184 | 185 | def test_flow_with_limit(self): 186 | """Test max flow with flow limit""" 187 | g = maxflow.mf_graph(3) 188 | g.add_edge(0, 1, 100) 189 | g.add_edge(1, 2, 100) 190 | 191 | # Test flow with limit 192 | limited_flow = g.flow(0, 2, 50) 193 | self.assertEqual(limited_flow, 50) 194 | 195 | # Check remaining capacity 196 | total_flow = g.flow(0, 2) 197 | self.assertEqual(total_flow, 50) # Should be 50 more 198 | 199 | def test_self_loop(self): 200 | """Test handling of self-loops""" 201 | g = maxflow.mf_graph(2) 202 | g.add_edge(0, 0, 10) # Self-loop 203 | g.add_edge(0, 1, 5) 204 | 205 | flow = g.flow(0, 1) 206 | self.assertEqual(flow, 5) 207 | 208 | 209 | if __name__ == "__main__": 210 | unittest.main() 211 | -------------------------------------------------------------------------------- /tests/test_scc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import scc 10 | 11 | 12 | class TestSCC(unittest.TestCase): 13 | """Test cases for scc module""" 14 | 15 | def practice2_g(self, N, M, edges, ans): 16 | res = scc.scc(N, edges) 17 | self.assertEqual(res, ans) 18 | 19 | def test_practice2_g(self): 20 | self.practice2_g( 21 | 6, 22 | 7, 23 | [(1, 4), (5, 2), (3, 0), (5, 5), (4, 1), (0, 3), (4, 2)], 24 | [[5], [1, 4], [2], [0, 3]], 25 | ) 26 | 27 | def test_single_node_no_edges(self): 28 | """Test single node with no edges""" 29 | result = scc.scc(1, []) 30 | self.assertEqual(result, [[0]]) 31 | 32 | def test_multiple_nodes_no_edges(self): 33 | """Test multiple nodes with no edges""" 34 | result = scc.scc(3, []) 35 | self.assertEqual(result, [[2], [1], [0]]) 36 | 37 | def test_simple_cycle(self): 38 | """Test simple cycle: 0 -> 1 -> 2 -> 0""" 39 | edges = [(0, 1), (1, 2), (2, 0)] 40 | result = scc.scc(3, edges) 41 | self.assertEqual(result, [[0, 1, 2]]) 42 | 43 | def test_chain_no_cycle(self): 44 | """Test chain with no cycle: 0 -> 1 -> 2""" 45 | edges = [(0, 1), (1, 2)] 46 | result = scc.scc(3, edges) 47 | self.assertEqual(result, [[0], [1], [2]]) 48 | 49 | def test_two_separate_cycles(self): 50 | """Test two separate cycles: (0->1->0) and (2->3->2)""" 51 | edges = [(0, 1), (1, 0), (2, 3), (3, 2)] 52 | result = scc.scc(4, edges) 53 | # Sort each component and the components list for consistent comparison 54 | result_sorted = [sorted(component) for component in result] 55 | result_sorted.sort() 56 | expected = [[0, 1], [2, 3]] 57 | self.assertEqual(result_sorted, expected) 58 | 59 | def test_self_loop(self): 60 | """Test node with self-loop""" 61 | edges = [(0, 0)] 62 | result = scc.scc(1, edges) 63 | self.assertEqual(result, [[0]]) 64 | 65 | def test_complex_graph(self): 66 | """Test complex graph with multiple SCCs""" 67 | # Graph: 0->1->2->3->1, 4->5->4, 6 (isolated) 68 | edges = [(0, 1), (1, 2), (2, 3), (3, 1), (4, 5), (5, 4)] 69 | result = scc.scc(7, edges) 70 | 71 | # Find components by checking which nodes are in the same component 72 | result_dict = {} 73 | for i, component in enumerate(result): 74 | for node in component: 75 | result_dict[node] = i 76 | 77 | # Check that nodes 1, 2, 3 are in the same component 78 | self.assertEqual(result_dict[1], result_dict[2]) 79 | self.assertEqual(result_dict[2], result_dict[3]) 80 | 81 | # Check that nodes 4, 5 are in the same component 82 | self.assertEqual(result_dict[4], result_dict[5]) 83 | 84 | # Check that 0 and 6 are in separate components 85 | self.assertNotEqual(result_dict[0], result_dict[1]) 86 | self.assertNotEqual(result_dict[6], result_dict[0]) 87 | self.assertNotEqual(result_dict[6], result_dict[1]) 88 | self.assertNotEqual(result_dict[6], result_dict[4]) 89 | 90 | def test_star_graph(self): 91 | """Test star graph (one node connected to all others)""" 92 | # 0 -> 1, 0 -> 2, 0 -> 3 (no cycles) 93 | edges = [(0, 1), (0, 2), (0, 3)] 94 | result = scc.scc(4, edges) 95 | self.assertEqual(result, [[0], [1], [2], [3]]) 96 | 97 | def test_complete_graph(self): 98 | """Test complete graph (all nodes connected to each other)""" 99 | edges = [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] 100 | result = scc.scc(3, edges) 101 | self.assertEqual(result, [[0, 1, 2]]) 102 | 103 | def test_empty_graph(self): 104 | """Test empty graph (no nodes)""" 105 | result = scc.scc(0, []) 106 | self.assertEqual(result, []) 107 | 108 | def test_large_chain(self): 109 | """Test large chain graph""" 110 | n = 10 111 | edges = [(i, i + 1) for i in range(n - 1)] 112 | result = scc.scc(n, edges) 113 | # Each node should be in its own component in forward order 114 | expected = [[i] for i in range(n)] 115 | self.assertEqual(result, expected) 116 | 117 | def test_multiple_self_loops(self): 118 | """Test multiple nodes with self-loops""" 119 | edges = [(0, 0), (1, 1), (2, 2)] 120 | result = scc.scc(3, edges) 121 | self.assertEqual(result, [[2], [1], [0]]) 122 | 123 | def test_bidirectional_edges(self): 124 | """Test graph with bidirectional edges""" 125 | # 0 <-> 1, 2 <-> 3, 0 -> 2 126 | edges = [(0, 1), (1, 0), (2, 3), (3, 2), (0, 2)] 127 | result = scc.scc(4, edges) 128 | 129 | # Check that 0,1 are in same component and 2,3 are in same component 130 | result_dict = {} 131 | for i, component in enumerate(result): 132 | for node in component: 133 | result_dict[node] = i 134 | 135 | self.assertEqual(result_dict[0], result_dict[1]) 136 | self.assertEqual(result_dict[2], result_dict[3]) 137 | self.assertNotEqual(result_dict[0], result_dict[2]) 138 | 139 | def test_weakly_connected_components(self): 140 | """Test graph that is weakly connected but has multiple SCCs""" 141 | # 0 -> 1 -> 2 -> 3, 3 -> 4 -> 5 -> 3 (cycle in 3,4,5) 142 | edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 3)] 143 | result = scc.scc(6, edges) 144 | 145 | # Find which nodes are in the same components 146 | result_dict = {} 147 | for i, component in enumerate(result): 148 | for node in component: 149 | result_dict[node] = i 150 | 151 | # Nodes 3, 4, 5 should be in the same component 152 | self.assertEqual(result_dict[3], result_dict[4]) 153 | self.assertEqual(result_dict[4], result_dict[5]) 154 | 155 | # Nodes 0, 1, 2 should be in separate components 156 | self.assertNotEqual(result_dict[0], result_dict[1]) 157 | self.assertNotEqual(result_dict[1], result_dict[2]) 158 | self.assertNotEqual(result_dict[0], result_dict[3]) 159 | 160 | def test_topological_order(self): 161 | """Test that SCCs are returned in topological order""" 162 | # DAG of SCCs: SCC{0} -> SCC{1,2} -> SCC{3} 163 | edges = [(0, 1), (1, 2), (2, 1), (1, 3), (2, 3)] 164 | result = scc.scc(4, edges) 165 | 166 | # Find positions in the result 167 | position = {} 168 | for i, component in enumerate(result): 169 | for node in component: 170 | position[node] = i 171 | 172 | # Check topological order: 0 should come before 1,2 which should come before 3 173 | self.assertLess(position[0], position[1]) 174 | self.assertLess(position[0], position[2]) 175 | self.assertLess(position[1], position[3]) 176 | self.assertLess(position[2], position[3]) 177 | 178 | # 1 and 2 should be in the same component 179 | self.assertEqual(position[1], position[2]) 180 | 181 | 182 | if __name__ == "__main__": 183 | unittest.main() 184 | -------------------------------------------------------------------------------- /convolution.py: -------------------------------------------------------------------------------- 1 | class FFT: 2 | def primitive_root_constexpr(self, m): 3 | if m == 2: 4 | return 1 5 | if m == 167772161: 6 | return 3 7 | if m == 469762049: 8 | return 3 9 | if m == 754974721: 10 | return 11 11 | if m == 998244353: 12 | return 3 13 | divs = [0] * 20 14 | divs[0] = 2 15 | cnt = 1 16 | x = (m - 1) // 2 17 | while x % 2 == 0: 18 | x //= 2 19 | i = 3 20 | while i * i <= x: 21 | if x % i == 0: 22 | divs[cnt] = i 23 | cnt += 1 24 | while x % i == 0: 25 | x //= i 26 | i += 2 27 | if x > 1: 28 | divs[cnt] = x 29 | cnt += 1 30 | g = 2 31 | while 1: 32 | ok = True 33 | for i in range(cnt): 34 | if pow(g, (m - 1) // divs[i], m) == 1: 35 | ok = False 36 | break 37 | if ok: 38 | return g 39 | g += 1 40 | 41 | def bsf(self, x): 42 | res = 0 43 | while x % 2 == 0: 44 | res += 1 45 | x //= 2 46 | return res 47 | 48 | rank2 = 0 49 | root = [] 50 | iroot = [] 51 | rate2 = [] 52 | irate2 = [] 53 | rate3 = [] 54 | irate3 = [] 55 | 56 | def __init__(self, MOD): 57 | self.mod = MOD 58 | self.g = self.primitive_root_constexpr(self.mod) 59 | self.rank2 = self.bsf(self.mod - 1) 60 | self.root = [0 for i in range(self.rank2 + 1)] 61 | self.iroot = [0 for i in range(self.rank2 + 1)] 62 | self.rate2 = [0 for i in range(self.rank2)] 63 | self.irate2 = [0 for i in range(self.rank2)] 64 | self.rate3 = [0 for i in range(self.rank2 - 1)] 65 | self.irate3 = [0 for i in range(self.rank2 - 1)] 66 | self.root[self.rank2] = pow(self.g, (self.mod - 1) >> self.rank2, self.mod) 67 | self.iroot[self.rank2] = pow(self.root[self.rank2], self.mod - 2, self.mod) 68 | for i in range(self.rank2 - 1, -1, -1): 69 | self.root[i] = (self.root[i + 1] ** 2) % self.mod 70 | self.iroot[i] = (self.iroot[i + 1] ** 2) % self.mod 71 | prod = 1 72 | iprod = 1 73 | for i in range(self.rank2 - 1): 74 | self.rate2[i] = (self.root[i + 2] * prod) % self.mod 75 | self.irate2[i] = (self.iroot[i + 2] * iprod) % self.mod 76 | prod = (prod * self.iroot[i + 2]) % self.mod 77 | iprod = (iprod * self.root[i + 2]) % self.mod 78 | prod = 1 79 | iprod = 1 80 | for i in range(self.rank2 - 2): 81 | self.rate3[i] = (self.root[i + 3] * prod) % self.mod 82 | self.irate3[i] = (self.iroot[i + 3] * iprod) % self.mod 83 | prod = (prod * self.iroot[i + 3]) % self.mod 84 | iprod = (iprod * self.root[i + 3]) % self.mod 85 | 86 | def butterfly(self, a): 87 | n = len(a) 88 | h = (n - 1).bit_length() 89 | 90 | LEN = 0 91 | while LEN < h: 92 | if h - LEN == 1: 93 | p = 1 << (h - LEN - 1) 94 | rot = 1 95 | for s in range(1 << LEN): 96 | offset = s << (h - LEN) 97 | for i in range(p): 98 | l = a[i + offset] 99 | r = a[i + offset + p] * rot 100 | a[i + offset] = (l + r) % self.mod 101 | a[i + offset + p] = (l - r) % self.mod 102 | rot *= self.rate2[(~s & -~s).bit_length() - 1] 103 | rot %= self.mod 104 | LEN += 1 105 | else: 106 | p = 1 << (h - LEN - 2) 107 | rot = 1 108 | imag = self.root[2] 109 | for s in range(1 << LEN): 110 | rot2 = (rot * rot) % self.mod 111 | rot3 = (rot2 * rot) % self.mod 112 | offset = s << (h - LEN) 113 | for i in range(p): 114 | a0 = a[i + offset] 115 | a1 = a[i + offset + p] * rot 116 | a2 = a[i + offset + 2 * p] * rot2 117 | a3 = a[i + offset + 3 * p] * rot3 118 | a1na3imag = (a1 - a3) % self.mod * imag 119 | a[i + offset] = (a0 + a2 + a1 + a3) % self.mod 120 | a[i + offset + p] = (a0 + a2 - a1 - a3) % self.mod 121 | a[i + offset + 2 * p] = (a0 - a2 + a1na3imag) % self.mod 122 | a[i + offset + 3 * p] = (a0 - a2 - a1na3imag) % self.mod 123 | rot *= self.rate3[(~s & -~s).bit_length() - 1] 124 | rot %= self.mod 125 | LEN += 2 126 | 127 | def butterfly_inv(self, a): 128 | n = len(a) 129 | h = (n - 1).bit_length() 130 | LEN = h 131 | while LEN: 132 | if LEN == 1: 133 | p = 1 << (h - LEN) 134 | irot = 1 135 | for s in range(1 << (LEN - 1)): 136 | offset = s << (h - LEN + 1) 137 | for i in range(p): 138 | l = a[i + offset] 139 | r = a[i + offset + p] 140 | a[i + offset] = (l + r) % self.mod 141 | a[i + offset + p] = (l - r) * irot % self.mod 142 | irot *= self.irate2[(~s & -~s).bit_length() - 1] 143 | irot %= self.mod 144 | LEN -= 1 145 | else: 146 | p = 1 << (h - LEN) 147 | irot = 1 148 | iimag = self.iroot[2] 149 | for s in range(1 << (LEN - 2)): 150 | irot2 = (irot * irot) % self.mod 151 | irot3 = (irot * irot2) % self.mod 152 | offset = s << (h - LEN + 2) 153 | for i in range(p): 154 | a0 = a[i + offset] 155 | a1 = a[i + offset + p] 156 | a2 = a[i + offset + 2 * p] 157 | a3 = a[i + offset + 3 * p] 158 | a2na3iimag = (a2 - a3) * iimag % self.mod 159 | a[i + offset] = (a0 + a1 + a2 + a3) % self.mod 160 | a[i + offset + p] = (a0 - a1 + a2na3iimag) * irot % self.mod 161 | a[i + offset + 2 * p] = (a0 + a1 - a2 - a3) * irot2 % self.mod 162 | a[i + offset + 3 * p] = ( 163 | (a0 - a1 - a2na3iimag) * irot3 % self.mod 164 | ) 165 | irot *= self.irate3[(~s & -~s).bit_length() - 1] 166 | irot %= self.mod 167 | LEN -= 2 168 | 169 | def convolution(self, a, b): 170 | n = len(a) 171 | m = len(b) 172 | if not (a) or not (b): 173 | return [] 174 | if min(n, m) <= 40: 175 | res = [0] * (n + m - 1) 176 | for i in range(n): 177 | for j in range(m): 178 | res[i + j] += a[i] * b[j] 179 | res[i + j] %= self.mod 180 | return res 181 | z = 1 << ((n + m - 2).bit_length()) 182 | a = a + [0] * (z - n) 183 | b = b + [0] * (z - m) 184 | self.butterfly(a) 185 | self.butterfly(b) 186 | c = [(a[i] * b[i]) % self.mod for i in range(z)] 187 | self.butterfly_inv(c) 188 | iz = pow(z, self.mod - 2, self.mod) 189 | for i in range(n + m - 1): 190 | c[i] = (c[i] * iz) % self.mod 191 | return c[: n + m - 1] 192 | -------------------------------------------------------------------------------- /fps.py: -------------------------------------------------------------------------------- 1 | class FPS: 2 | root = ( 3 | 1, 4 | 998244352, 5 | 911660635, 6 | 372528824, 7 | 929031873, 8 | 452798380, 9 | 922799308, 10 | 781712469, 11 | 476477967, 12 | 166035806, 13 | 258648936, 14 | 584193783, 15 | 63912897, 16 | 350007156, 17 | 666702199, 18 | 968855178, 19 | 629671588, 20 | 24514907, 21 | 996173970, 22 | 363395222, 23 | 565042129, 24 | 733596141, 25 | 267099868, 26 | 15311432, 27 | ) 28 | iroot = ( 29 | 1, 30 | 998244352, 31 | 86583718, 32 | 509520358, 33 | 337190230, 34 | 87557064, 35 | 609441965, 36 | 135236158, 37 | 304459705, 38 | 685443576, 39 | 381598368, 40 | 335559352, 41 | 129292727, 42 | 358024708, 43 | 814576206, 44 | 708402881, 45 | 283043518, 46 | 3707709, 47 | 121392023, 48 | 704923114, 49 | 950391366, 50 | 428961804, 51 | 382752275, 52 | 469870224, 53 | ) 54 | rate2 = ( 55 | 911660635, 56 | 509520358, 57 | 369330050, 58 | 332049552, 59 | 983190778, 60 | 123842337, 61 | 238493703, 62 | 975955924, 63 | 603855026, 64 | 856644456, 65 | 131300601, 66 | 842657263, 67 | 730768835, 68 | 942482514, 69 | 806263778, 70 | 151565301, 71 | 510815449, 72 | 503497456, 73 | 743006876, 74 | 741047443, 75 | 56250497, 76 | 867605899, 77 | 0, 78 | ) 79 | irate2 = ( 80 | 86583718, 81 | 372528824, 82 | 373294451, 83 | 645684063, 84 | 112220581, 85 | 692852209, 86 | 155456985, 87 | 797128860, 88 | 90816748, 89 | 860285882, 90 | 927414960, 91 | 354738543, 92 | 109331171, 93 | 293255632, 94 | 535113200, 95 | 308540755, 96 | 121186627, 97 | 608385704, 98 | 438932459, 99 | 359477183, 100 | 824071951, 101 | 103369235, 102 | 0, 103 | ) 104 | rate3 = ( 105 | 372528824, 106 | 337190230, 107 | 454590761, 108 | 816400692, 109 | 578227951, 110 | 180142363, 111 | 83780245, 112 | 6597683, 113 | 70046822, 114 | 623238099, 115 | 183021267, 116 | 402682409, 117 | 631680428, 118 | 344509872, 119 | 689220186, 120 | 365017329, 121 | 774342554, 122 | 729444058, 123 | 102986190, 124 | 128751033, 125 | 395565204, 126 | 0, 127 | ) 128 | irate3 = ( 129 | 509520358, 130 | 929031873, 131 | 170256584, 132 | 839780419, 133 | 282974284, 134 | 395914482, 135 | 444904435, 136 | 72135471, 137 | 638914820, 138 | 66769500, 139 | 771127074, 140 | 985925487, 141 | 262319669, 142 | 262341272, 143 | 625870173, 144 | 768022760, 145 | 859816005, 146 | 914661783, 147 | 430819711, 148 | 272774365, 149 | 530924681, 150 | 0, 151 | ) 152 | mod = 998244353 153 | Func = [0] 154 | 155 | def __init__(self, L): 156 | self.Func = [x % self.mod for x in L] 157 | 158 | def butterfly(self, a): 159 | n = len(a) 160 | h = (n - 1).bit_length() 161 | 162 | LEN = 0 163 | while LEN < h: 164 | if h - LEN == 1: 165 | p = 1 << (h - LEN - 1) 166 | rot = 1 167 | for s in range(1 << LEN): 168 | offset = s << (h - LEN) 169 | for i in range(p): 170 | l = a[i + offset] 171 | r = a[i + offset + p] * rot 172 | a[i + offset] = (l + r) % self.mod 173 | a[i + offset + p] = (l - r) % self.mod 174 | rot *= self.rate2[(~s & -~s).bit_length() - 1] 175 | rot %= self.mod 176 | LEN += 1 177 | else: 178 | p = 1 << (h - LEN - 2) 179 | rot = 1 180 | imag = self.root[2] 181 | for s in range(1 << LEN): 182 | rot2 = (rot * rot) % self.mod 183 | rot3 = (rot2 * rot) % self.mod 184 | offset = s << (h - LEN) 185 | for i in range(p): 186 | a0 = a[i + offset] 187 | a1 = a[i + offset + p] * rot 188 | a2 = a[i + offset + 2 * p] * rot2 189 | a3 = a[i + offset + 3 * p] * rot3 190 | a1na3imag = (a1 - a3) % self.mod * imag 191 | a[i + offset] = (a0 + a2 + a1 + a3) % self.mod 192 | a[i + offset + p] = (a0 + a2 - a1 - a3) % self.mod 193 | a[i + offset + 2 * p] = (a0 - a2 + a1na3imag) % self.mod 194 | a[i + offset + 3 * p] = (a0 - a2 - a1na3imag) % self.mod 195 | rot *= self.rate3[(~s & -~s).bit_length() - 1] 196 | rot %= self.mod 197 | LEN += 2 198 | return a 199 | 200 | def butterfly_inv(self, a): 201 | n = len(a) 202 | h = (n - 1).bit_length() 203 | LEN = h 204 | while LEN: 205 | if LEN == 1: 206 | p = 1 << (h - LEN) 207 | irot = 1 208 | for s in range(1 << (LEN - 1)): 209 | offset = s << (h - LEN + 1) 210 | for i in range(p): 211 | l = a[i + offset] 212 | r = a[i + offset + p] 213 | a[i + offset] = (l + r) % self.mod 214 | a[i + offset + p] = (l - r) * irot % self.mod 215 | irot *= self.irate2[(~s & -~s).bit_length() - 1] 216 | irot %= self.mod 217 | LEN -= 1 218 | else: 219 | p = 1 << (h - LEN) 220 | irot = 1 221 | iimag = self.iroot[2] 222 | for s in range(1 << (LEN - 2)): 223 | irot2 = (irot * irot) % self.mod 224 | irot3 = (irot * irot2) % self.mod 225 | offset = s << (h - LEN + 2) 226 | for i in range(p): 227 | a0 = a[i + offset] 228 | a1 = a[i + offset + p] 229 | a2 = a[i + offset + 2 * p] 230 | a3 = a[i + offset + 3 * p] 231 | a2na3iimag = (a2 - a3) * iimag % self.mod 232 | a[i + offset] = (a0 + a1 + a2 + a3) % self.mod 233 | a[i + offset + p] = (a0 - a1 + a2na3iimag) * irot % self.mod 234 | a[i + offset + 2 * p] = (a0 + a1 - a2 - a3) * irot2 % self.mod 235 | a[i + offset + 3 * p] = ( 236 | (a0 - a1 - a2na3iimag) * irot3 % self.mod 237 | ) 238 | irot *= self.irate3[(~s & -~s).bit_length() - 1] 239 | irot %= self.mod 240 | LEN -= 2 241 | return a 242 | 243 | def __mul__(self, other): 244 | if type(other) == int: 245 | ret = [(x * other) % self.mod for x in self.Func] 246 | return FPS(ret) 247 | a = self.Func 248 | b = other.Func 249 | n = len(a) 250 | m = len(b) 251 | if not (a) or not (b): 252 | return FPS([]) 253 | if min(n, m) <= 40: 254 | if n < m: 255 | n, m = m, n 256 | a, b = b, a 257 | res = [0] * (n + m - 1) 258 | for i in range(n): 259 | for j in range(m): 260 | res[i + j] += a[i] * b[j] 261 | res[i + j] %= self.mod 262 | return FPS(res) 263 | z = 1 << ((n + m - 2).bit_length()) 264 | a = a + [0] * (z - n) 265 | b = b + [0] * (z - m) 266 | a = self.butterfly(a) 267 | b = self.butterfly(b) 268 | c = [(a[i] * b[i]) % self.mod for i in range(z)] 269 | self.butterfly_inv(c) 270 | iz = pow(z, self.mod - 2, self.mod) 271 | for i in range(n + m - 1): 272 | c[i] = (c[i] * iz) % self.mod 273 | return FPS(c[: n + m - 1]) 274 | 275 | def __imul__(self, other): 276 | self = self * other 277 | return self 278 | 279 | def __add__(self, other): 280 | res = [0 for i in range(max(len(self.Func), len(other.Func)))] 281 | for i, x in enumerate(self.Func): 282 | res[i] += x 283 | res[i] %= self.mod 284 | for i, x in enumerate(other.Func): 285 | res[i] += x 286 | res[i] %= self.mod 287 | return FPS(res) 288 | 289 | def __iadd__(self, other): 290 | self = self + other 291 | return self 292 | 293 | def __sub__(self, other): 294 | res = [0 for i in range(max(len(self.Func), len(other.Func)))] 295 | for i, x in enumerate(self.Func): 296 | res[i] += x 297 | res[i] %= self.mod 298 | for i, x in enumerate(other.Func): 299 | res[i] -= x 300 | res[i] %= self.mod 301 | return FPS(res) 302 | 303 | def __isub__(self, other): 304 | self = self - other 305 | return self 306 | 307 | def inv(self, d=-1): 308 | n = len(self.Func) 309 | assert n != 0 and self.Func[0] != 0 310 | if d == -1: 311 | d = n 312 | assert d > 0 313 | res = [pow(self.Func[0], self.mod - 2, self.mod)] 314 | while len(res) < d: 315 | m = len(res) 316 | f = [self.Func[i] for i in range(min(n, 2 * m))] 317 | r = res[:] 318 | 319 | if len(f) < 2 * m: 320 | f += [0] * (2 * m - len(f)) 321 | elif len(f) > 2 * m: 322 | f = f[: 2 * m] 323 | if len(r) < 2 * m: 324 | r += [0] * (2 * m - len(r)) 325 | elif len(r) > 2 * m: 326 | r = r[: 2 * m] 327 | f = self.butterfly(f) 328 | r = self.butterfly(r) 329 | for i in range(2 * m): 330 | f[i] *= r[i] 331 | f[i] %= self.mod 332 | f = self.butterfly_inv(f) 333 | f = f[m:] 334 | if len(f) < 2 * m: 335 | f += [0] * (2 * m - len(f)) 336 | elif len(f) > 2 * m: 337 | f = f[: 2 * m] 338 | f = self.butterfly(f) 339 | for i in range(2 * m): 340 | f[i] *= r[i] 341 | f[i] %= self.mod 342 | f = self.butterfly_inv(f) 343 | iz = pow(2 * m, self.mod - 2, self.mod) 344 | iz *= -iz 345 | iz %= self.mod 346 | for i in range(m): 347 | f[i] *= iz 348 | f[i] %= self.mod 349 | res += f[:m] 350 | return FPS(res[:d]) 351 | 352 | def __truediv__(self, other): 353 | if type(other) == int: 354 | invother = pow(other, self.mod - 2, self.mod) 355 | ret = [(x * invother) % self.mod for x in self.Func] 356 | return FPS(ret) 357 | assert other.Func[0] != 0 358 | return self * (other.inv()) 359 | 360 | def __floordiv__(self, other): 361 | if not other: 362 | raise ZeroDivisionError 363 | if isinstance(other, int): 364 | return self / other 365 | self.reduce() 366 | other.reduce() 367 | F_deg = len(self.Func) - 1 368 | G_deg = len(other.Func) - 1 369 | if F_deg < G_deg: 370 | return FPS([]) 371 | m = F_deg - G_deg + 1 372 | F_inverse = self.Func[::-1] 373 | G_inverse = other.Func[::-1] 374 | ans = (FPS(F_inverse) * (FPS(G_inverse).inv(d=m))).Func 375 | return FPS(ans[m - 1 :: -1]) 376 | 377 | def __bool__(self): 378 | return any(self.Func) 379 | 380 | def __mod__(self, other): 381 | if not other: 382 | raise ZeroDivisionError 383 | self.reduce() 384 | other.reduce() 385 | FG = self // other 386 | ans = self - FG * other 387 | ans.reduce() 388 | return ans 389 | 390 | def reduce(self): 391 | if self: 392 | while not self.Func[-1]: 393 | self.Func.pop() 394 | else: 395 | self.Func = [] 396 | 397 | def __itruediv__(self, other): 398 | self = self / other 399 | return self 400 | 401 | def __lshift__(self, d): 402 | return FPS([0] * d + self.Func) 403 | 404 | def __ilshift__(self, d): 405 | self = self << d 406 | return self 407 | 408 | def __rshift__(self, d): 409 | n = len(self.Func) 410 | return FPS(self.Func[min(n, d) :] + [0] * min(n, d)) 411 | 412 | def __irshift__(self, d): 413 | self = self >> d 414 | return self 415 | 416 | def __lt__(self, other): 417 | if len(self.Func) < len(other.Func): 418 | return True 419 | elif len(self.Func) > len(other.Func): 420 | return True 421 | else: 422 | return self.Func < other.Func 423 | 424 | def __gt__(self, other): 425 | if len(self.Func) > len(other.Func): 426 | return True 427 | elif len(self.Func) < len(other.Func): 428 | return True 429 | else: 430 | return self.Func > other.Func 431 | 432 | def __eq__(self, other): 433 | return self.Func == other.Func 434 | 435 | def __le__(self, other): 436 | if len(self.Func) < len(other.Func): 437 | return True 438 | elif len(self.Func) > len(other.Func): 439 | return True 440 | else: 441 | return self.Func <= other.Func 442 | 443 | def __ge__(self, other): 444 | if len(self.Func) > len(other.Func): 445 | return True 446 | elif len(self.Func) < len(other.Func): 447 | return True 448 | else: 449 | return self.Func > other.Func 450 | 451 | def __ne__(self, other): 452 | return self.Func != other.Func 453 | 454 | def __str__(self): 455 | return f"FPS({self.Func})" 456 | 457 | def __repr__(self): 458 | return f"FPS({self.Func})" 459 | 460 | def diff(self): 461 | n = len(self.Func) 462 | ret = [0 for i in range(max(0, n - 1))] 463 | for i in range(1, n): 464 | ret[i - 1] = (self.Func[i] * i) % self.mod 465 | return FPS(ret) 466 | 467 | def integral(self): 468 | n = len(self.Func) 469 | ret = [0 for i in range(n + 1)] 470 | for i in range(n): 471 | ret[i + 1] = self.Func[i] * pow(i + 1, self.mod - 2, self.mod) % self.mod 472 | return FPS(ret) 473 | 474 | def log(self, deg=-1): 475 | assert self.Func[0] == 1 476 | n = len(self.Func) 477 | if deg == -1: 478 | deg = n 479 | return (self.diff() * self.inv()).integral() 480 | 481 | def mod_sqrt(self, a): 482 | p = self.mod 483 | assert 0 <= a and a < p 484 | if a < 2: 485 | return a 486 | if pow(a, (p - 1) // 2, p) != 1: 487 | return -1 488 | b = 1 489 | one = 1 490 | while pow(b, (p - 1) >> 1, p) == 1: 491 | b += one 492 | m = p - 1 493 | e = 0 494 | while m % 2 == 0: 495 | m >>= 1 496 | e += 1 497 | x = pow(a, (m - 1) >> 1, p) 498 | y = (a * x * x) % p 499 | x *= a 500 | x %= p 501 | z = pow(b, m, p) 502 | while y != 1: 503 | j = 0 504 | t = y 505 | while t != one: 506 | j += 1 507 | t *= t 508 | t %= p 509 | z = pow(z, 1 << (e - j - 1), p) 510 | x *= z 511 | x %= p 512 | z *= z 513 | z %= p 514 | y *= z 515 | y %= p 516 | e = j 517 | return x 518 | 519 | def sqrt(self, deg=-1): 520 | n = len(self.Func) 521 | if deg == -1: 522 | deg = n 523 | if n == 0: 524 | return FPS([0 for i in range(deg)]) 525 | if self.Func[0] == 0: 526 | for i in range(1, n): 527 | if self.Func[i] != 0: 528 | if i & 1: 529 | return FPS([]) 530 | if deg - i // 2 <= 0: 531 | break 532 | ret = (self >> i).sqrt(deg - i // 2) 533 | if len(ret.Func) == 0: 534 | return FPS([]) 535 | ret = ret << (i // 2) 536 | if len(ret.Func) < deg: 537 | ret.Func += [0] * (deg - len(ret.Func)) 538 | return ret 539 | return FPS([0] * deg) 540 | sqr = self.mod_sqrt(self.Func[0]) 541 | if sqr == -1: 542 | return FPS([]) 543 | assert sqr * sqr % self.mod == self.Func[0] 544 | ret = FPS([sqr]) 545 | inv2 = (self.mod + 1) // 2 546 | i = 1 547 | while i < deg: 548 | ret = (ret + FPS(self.Func[: i << 1]) * ret.inv(i << 1)) * inv2 549 | i <<= 1 550 | self = FPS(ret.Func[:deg]) 551 | return self 552 | 553 | def resize(self, deg): 554 | if len(self.Func) < deg: 555 | return FPS(self.Func + [0] * (deg - len(self.Func))) 556 | elif len(self.Func) > deg: 557 | return FPS(self.Func[:deg]) 558 | else: 559 | return FPS(self.Func) 560 | 561 | def exp(self, deg=-1): 562 | n = len(self.Func) 563 | assert n > 0 and self.Func[0] == 0 564 | if deg == -1: 565 | deg = n 566 | assert deg >= 0 567 | g = [1] 568 | g_fft = [1, 1] 569 | ret = FPS(self.Func[:]) 570 | ret.Func[0] = 1 571 | ret.resize(deg) 572 | h_drv = ret.diff() 573 | m = 2 574 | while m < deg: 575 | f_fft = ret.Func[:m] + [0] * m 576 | ret.butterfly(f_fft) 577 | 578 | # step 2.a 579 | _g = [f_fft[i] * g_fft[i] % ret.mod for i in range(m)] 580 | ret.butterfly_inv(_g) 581 | _g = _g[m // 2 : m] + [0] * (m // 2) 582 | ret.butterfly(_g) 583 | for i in range(m): 584 | _g[i] *= g_fft[i] 585 | _g[i] %= ret.mod 586 | ret.butterfly_inv(_g) 587 | tmp = pow(-m * m, ret.mod - 2, ret.mod) 588 | for i in range(m): 589 | _g[i] *= tmp 590 | _g[i] %= ret.mod 591 | g += _g[: m // 2] 592 | # step 2.b--2.d 593 | t = FPS(ret.Func[:m]).diff() 594 | r = h_drv.Func[: m - 1] + [0] 595 | ret.butterfly(r) 596 | for i in range(m): 597 | r[i] *= f_fft[i] 598 | r[i] %= ret.mod 599 | ret.butterfly_inv(r) 600 | tmp = pow(-m, ret.mod - 2, ret.mod) 601 | for i in range(m): 602 | r[i] *= tmp 603 | r[i] %= ret.mod 604 | t = (t + FPS(r)).Func 605 | t = [t[-1]] + t 606 | t.pop() 607 | # step 2.e 608 | if 2 * m < deg: 609 | if len(t) < 2 * m: 610 | t += [0] * (2 * m - len(t)) 611 | elif len(t) > 2 * m: 612 | t = t[: 2 * m] 613 | ret.butterfly(t) 614 | g_fft = g[:] 615 | if len(g_fft) < 2 * m: 616 | g_fft += [0] * (2 * m - len(g_fft)) 617 | elif len(g_fft) > 2 * m: 618 | g_fft = g_fft[: 2 * m] 619 | ret.butterfly(g_fft) 620 | for i in range(2 * m): 621 | t[i] *= g_fft[i] 622 | t[i] %= ret.mod 623 | ret.butterfly_inv(t) 624 | tmp = pow(2 * m, ret.mod - 2, ret.mod) 625 | t = t[:m] 626 | for i in range(m): 627 | t[i] *= tmp 628 | t[i] %= ret.mod 629 | else: 630 | g1 = g[m // 2 :] 631 | s1 = t[m // 2 :] 632 | t = t[: m // 2] 633 | g1 += [0] * (m - len(g1)) 634 | s1 += [0] * (m - len(s1)) 635 | t += [0] * (m - len(t)) 636 | 637 | ret.butterfly(g1) 638 | ret.butterfly(t) 639 | ret.butterfly(s1) 640 | for i in range(m): 641 | s1[i] = (g_fft[i] * s1[i] + g1[i] * t[i]) % ret.mod 642 | for i in range(m): 643 | t[i] *= g_fft[i] 644 | t[i] %= ret.mod 645 | ret.butterfly_inv(t) 646 | ret.butterfly_inv(s1) 647 | for i in range(m // 2): 648 | t[i + m // 2] += s1[i] 649 | t[i + m // 2] %= ret.mod 650 | tmp = pow(m, ret.mod - 2, ret.mod) 651 | for i in range(m): 652 | t[i] *= tmp 653 | t[i] %= ret.mod 654 | # step 2.f 655 | v = ret.Func[m : min(deg, 2 * m)] + [0] * (2 * m - min(deg, 2 * m)) 656 | t = [0] * (m - 1) + t 657 | t = FPS(t).integral().Func 658 | for i in range(m): 659 | v[i] -= t[m + i] 660 | v[i] %= ret.mod 661 | # step 2.g 662 | if len(v) < 2 * m: 663 | v += [0] * (2 * m - len(v)) 664 | else: 665 | v = v[: 2 * m] 666 | ret.butterfly(v) 667 | for i in range(2 * m): 668 | v[i] *= f_fft[i] 669 | v[i] %= self.mod 670 | ret.butterfly_inv(v) 671 | v = v[:m] 672 | tmp = pow(2 * m, ret.mod - 2, ret.mod) 673 | for i in range(m): 674 | v[i] *= tmp 675 | v[i] %= ret.mod 676 | # step 2.h 677 | for i in range(min(deg - m, m)): 678 | ret.Func[m + i] = v[i] 679 | m *= 2 680 | return ret 681 | 682 | def powfps(self, k, deg=-1): 683 | a = self.Func[:] 684 | n = len(self.Func) 685 | if k == 0: 686 | return FPS([int(i == 0) for i in range(n)]) 687 | l = 0 688 | while l < len(a) and not a[l]: 689 | l += 1 690 | if l * k >= n: 691 | return FPS([0] * n) 692 | ic = pow(a[l], self.mod - 2, self.mod) 693 | pc = pow(a[l], k, self.mod) 694 | a = FPS([(a[i] * ic) % self.mod for i in range(l, len(a))]).log() 695 | a *= k 696 | a = a.exp() 697 | a *= pc 698 | a = [0] * (l * k) + a.Func[: n - l * k] 699 | return FPS(a) 700 | -------------------------------------------------------------------------------- /tests/test_prime_fact.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import unittest 6 | 7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | 9 | import prime_fact 10 | 11 | 12 | class TestPrimeFact(unittest.TestCase): 13 | """Test cases for prime_fact module""" 14 | 15 | def primefact_selfcheck(self, N): 16 | from collections import Counter 17 | 18 | PF = prime_fact.prime_fact(N) 19 | M = 1 20 | for p, e in PF.items(): 21 | M *= pow(p, e) 22 | self.assertEqual(N, M) 23 | 24 | def test_prime_factorization(self): 25 | for n in range(1, 10**5): 26 | self.primefact_selfcheck(n) 27 | 28 | for n in range(10**9, 10**9 + 1000): 29 | self.primefact_selfcheck(n) 30 | 31 | for n in range(10**17, 10**17 + 1000): 32 | self.primefact_selfcheck(n) 33 | 34 | def is_prime_example(self): 35 | numList = [1, 2, 3, 4, 998244353, 1000000000000000000] 36 | ansList = [False, True, True, False, True, False] 37 | for n, f in zip(numList, ansList): 38 | self.assertEqual(prime_fact.is_probable_prime(n), f) 39 | 40 | def is_prime_hackissue1325(self): 41 | numList = [ 42 | 10, 43 | 2047, 44 | 1373653, 45 | 9080191, 46 | 25326001, 47 | 3215031751, 48 | 4759123141, 49 | 1122004669633, 50 | 2152302898747, 51 | 3474749660383, 52 | 341550071728321, 53 | ] 54 | for n in numList: 55 | self.assertEqual(prime_fact.is_probable_prime(n), False) 56 | 57 | def is_prime_carmichael(self): 58 | numList = [ 59 | 1000, 60 | 1055384929, 61 | 37964809, 62 | 178482151, 63 | 4903921, 64 | 395044651, 65 | 721244161, 66 | 87318001, 67 | 455106601, 68 | 631071001, 69 | 2801124001, 70 | 45890209, 71 | 136625941, 72 | 496050841, 73 | 171679561, 74 | 1190790721, 75 | 252601, 76 | 854197345, 77 | 41041, 78 | 473847121, 79 | 1505432881, 80 | 133800661, 81 | 2693939401, 82 | 396262945, 83 | 129255841, 84 | 1699279441, 85 | 1776450565, 86 | 280067761, 87 | 1445084173, 88 | 2199931651, 89 | 3044970001, 90 | 1576826161, 91 | 683032801, 92 | 2140538401, 93 | 382536001, 94 | 172081, 95 | 1382114881, 96 | 633639097, 97 | 440707345, 98 | 517937581, 99 | 2797002901, 100 | 151813201, 101 | 1676203201, 102 | 1615681, 103 | 357380101, 104 | 64377991, 105 | 346808881, 106 | 1183104001, 107 | 1100674561, 108 | 2628073, 109 | 625482001, 110 | 1442761201, 111 | 92625121, 112 | 228842209, 113 | 1163659861, 114 | 8927101, 115 | 230996949, 116 | 395136505, 117 | 1214703721, 118 | 558977761, 119 | 101649241, 120 | 1955324449, 121 | 37280881, 122 | 488881, 123 | 23382529, 124 | 392099401, 125 | 13696033, 126 | 2178944461, 127 | 256828321, 128 | 93869665, 129 | 1836304561, 130 | 2301745249, 131 | 116682721, 132 | 38151361, 133 | 642708001, 134 | 2690867401, 135 | 902645857, 136 | 280761481, 137 | 390489121, 138 | 518706721, 139 | 1674309385, 140 | 8911, 141 | 2244932281, 142 | 1817067169, 143 | 1082809, 144 | 3078386641, 145 | 288120421, 146 | 449065, 147 | 70561921, 148 | 867800701, 149 | 1746692641, 150 | 1726372441, 151 | 14913991, 152 | 200753281, 153 | 1676641681, 154 | 2704801, 155 | 530881, 156 | 11921001, 157 | 1545387481, 158 | 1384157161, 159 | 314821, 160 | 101101, 161 | 2605557781, 162 | 1916729101, 163 | 1831048561, 164 | 2232385345, 165 | 115921, 166 | 38624041, 167 | 16046641, 168 | 1618206745, 169 | 629692801, 170 | 555465601, 171 | 1772267281, 172 | 99036001, 173 | 2414829781, 174 | 75681541, 175 | 602593441, 176 | 3044238121, 177 | 533860309, 178 | 843704401, 179 | 15888313, 180 | 2588653081, 181 | 246446929, 182 | 2811315361, 183 | 2539024741, 184 | 918661501, 185 | 1919767681, 186 | 2766172501, 187 | 26280073, 188 | 698548201, 189 | 118901521, 190 | 1574601601, 191 | 172290241, 192 | 208969201, 193 | 2467813621, 194 | 1152793621, 195 | 1206057601, 196 | 294409, 197 | 2302419601, 198 | 2212935985, 199 | 3828001, 200 | 1688639041, 201 | 65241793, 202 | 8719921, 203 | 8355841, 204 | 1708549501, 205 | 609865201, 206 | 1984089601, 207 | 1801558201, 208 | 4463641, 209 | 689880801, 210 | 109393201, 211 | 328573477, 212 | 34657141, 213 | 569332177, 214 | 842202361, 215 | 2617181281, 216 | 266003101, 217 | 9494101, 218 | 225745345, 219 | 527761081, 220 | 1773289, 221 | 1093916341, 222 | 2529410281, 223 | 977737321, 224 | 1571503801, 225 | 300614161, 226 | 544101481, 227 | 419520241, 228 | 2320224481, 229 | 3224065, 230 | 1698623641, 231 | 1318126321, 232 | 120981601, 233 | 10024561, 234 | 194120389, 235 | 1942608529, 236 | 1441316269, 237 | 490503601, 238 | 139592101, 239 | 977892241, 240 | 662086041, 241 | 133344793, 242 | 188516329, 243 | 3035837161, 244 | 1689411601, 245 | 1949646601, 246 | 10877581, 247 | 26719701, 248 | 84350561, 249 | 338740417, 250 | 1958102641, 251 | 230630401, 252 | 426821473, 253 | 2547621973, 254 | 2833846561, 255 | 8134561, 256 | 1269295201, 257 | 833608321, 258 | 1312332001, 259 | 2339165521, 260 | 31146661, 261 | 638959321, 262 | 1569457, 263 | 704934361, 264 | 46483633, 265 | 839275921, 266 | 2470348441, 267 | 81638401, 268 | 271794601, 269 | 1848112761, 270 | 189941761, 271 | 1992841201, 272 | 882796321, 273 | 705101761, 274 | 17236801, 275 | 60957361, 276 | 84417985, 277 | 809883361, 278 | 12261061, 279 | 552721, 280 | 606057985, 281 | 2509860961, 282 | 5049001, 283 | 19384289, 284 | 1461241, 285 | 1887933601, 286 | 958762729, 287 | 214850881, 288 | 53245921, 289 | 277241401, 290 | 891706861, 291 | 2443829641, 292 | 206955841, 293 | 161913961, 294 | 602426161, 295 | 1180398961, 296 | 139952671, 297 | 43331401, 298 | 968553181, 299 | 130032865, 300 | 1309440001, 301 | 35571601, 302 | 2806205689, 303 | 2170282969, 304 | 2332627249, 305 | 1035608041, 306 | 417241045, 307 | 2586927553, 308 | 997633, 309 | 5148001, 310 | 1855100017, 311 | 710541481, 312 | 41471521, 313 | 1705470481, 314 | 1778382541, 315 | 1483199641, 316 | 153927961, 317 | 115039081, 318 | 31692805, 319 | 161242705, 320 | 717164449, 321 | 1033669, 322 | 545570641, 323 | 1264145401, 324 | 168659569, 325 | 17098369, 326 | 2544590161, 327 | 561, 328 | 1520467201, 329 | 2598933481, 330 | 957044881, 331 | 702683101, 332 | 2049293401, 333 | 11205601, 334 | 1136739745, 335 | 939947009, 336 | 38637361, 337 | 403317421, 338 | 43584481, 339 | 2821, 340 | 2265650401, 341 | 184353001, 342 | 2359686241, 343 | 334783585, 344 | 1271325841, 345 | 3086434561, 346 | 834244501, 347 | 1216631521, 348 | 2642025673, 349 | 744866305, 350 | 44238481, 351 | 12945745, 352 | 2735309521, 353 | 167979421, 354 | 9585541, 355 | 1193229577, 356 | 2494621585, 357 | 10606681, 358 | 291848401, 359 | 258634741, 360 | 49333201, 361 | 134857801, 362 | 37167361, 363 | 1659935761, 364 | 360067201, 365 | 993905641, 366 | 849064321, 367 | 41298985, 368 | 860056705, 369 | 75765313, 370 | 54767881, 371 | 1362132541, 372 | 814056001, 373 | 146843929, 374 | 140241361, 375 | 7995169, 376 | 1103145121, 377 | 2048751901, 378 | 169057801, 379 | 178451857, 380 | 557795161, 381 | 5968873, 382 | 144218341, 383 | 1521835381, 384 | 1848681121, 385 | 82929001, 386 | 1268604001, 387 | 461854261, 388 | 36765901, 389 | 80282161, 390 | 434330401, 391 | 135556345, 392 | 1125038377, 393 | 683379841, 394 | 710382401, 395 | 34196401, 396 | 1988071801, 397 | 2140699681, 398 | 1648076041, 399 | 413058601, 400 | 194675041, 401 | 484662529, 402 | 2531845, 403 | 2396357041, 404 | 2616662881, 405 | 568227241, 406 | 157731841, 407 | 158864833, 408 | 18307381, 409 | 2508013, 410 | 88689601, 411 | 1785507361, 412 | 2527812001, 413 | 250200721, 414 | 2601144001, 415 | 824405041, 416 | 548871961, 417 | 2564889601, 418 | 9613297, 419 | 822531841, 420 | 658801, 421 | 2246916001, 422 | 590754385, 423 | 47006785, 424 | 1641323905, 425 | 1162202581, 426 | 67902031, 427 | 847491361, 428 | 836515681, 429 | 2489462641, 430 | 1795216501, 431 | 2176838049, 432 | 37354465, 433 | 2444950561, 434 | 1954174465, 435 | 1481619601, 436 | 3024774901, 437 | 130497361, 438 | 1140441121, 439 | 2101170097, 440 | 739444021, 441 | 1299963601, 442 | 1423668961, 443 | 6601, 444 | 18900973, 445 | 532758241, 446 | 247095361, 447 | 975303121, 448 | 545363281, 449 | 612347905, 450 | 362569201, 451 | 3007991701, 452 | 2216430721, 453 | 77826001, 454 | 169570801, 455 | 1321983937, 456 | 62756641, 457 | 557160241, 458 | 393716701, 459 | 271481329, 460 | 556450777, 461 | 151530401, 462 | 99830641, 463 | 492559141, 464 | 367804801, 465 | 1685266561, 466 | 15247621, 467 | 26921089, 468 | 529782121, 469 | 63973, 470 | 104852881, 471 | 1828377001, 472 | 2107535221, 473 | 1528936501, 474 | 461502097, 475 | 2607237361, 476 | 595405201, 477 | 3072080089, 478 | 804978721, 479 | 1909001, 480 | 962500561, 481 | 8341201, 482 | 518117041, 483 | 106041937, 484 | 3581761, 485 | 127664461, 486 | 1845871105, 487 | 1027334881, 488 | 181154701, 489 | 171454321, 490 | 188461, 491 | 1773486001, 492 | 1232469001, 493 | 2465, 494 | 1177195201, 495 | 440306461, 496 | 1784975941, 497 | 2100901, 498 | 483006889, 499 | 2597928961, 500 | 462199681, 501 | 2080544005, 502 | 2965085641, 503 | 1632785701, 504 | 1213619761, 505 | 1540454761, 506 | 945959365, 507 | 216821881, 508 | 52633, 509 | 3025708561, 510 | 1597821121, 511 | 684106401, 512 | 301704985, 513 | 129762001, 514 | 17586361, 515 | 6840001, 516 | 1207252621, 517 | 9439201, 518 | 434932961, 519 | 516684961, 520 | 6868261, 521 | 2561945401, 522 | 2367379201, 523 | 193708801, 524 | 2067887557, 525 | 511338241, 526 | 1198650961, 527 | 40280065, 528 | 2479305985, 529 | 838201, 530 | 11119105, 531 | 6313681, 532 | 1913016001, 533 | 580565233, 534 | 1132988545, 535 | 962442001, 536 | 477726145, 537 | 672389641, 538 | 182356993, 539 | 2470894273, 540 | 620169409, 541 | 981789337, 542 | 1918052065, 543 | 22665505, 544 | 2258118721, 545 | 851934601, 546 | 67994641, 547 | 238527745, 548 | 161035057, 549 | 1879480513, 550 | 212027401, 551 | 2322648901, 552 | 16778881, 553 | 841340521, 554 | 29111881, 555 | 790623289, 556 | 213835861, 557 | 2105594401, 558 | 1150270849, 559 | 83099521, 560 | 60112885, 561 | 2111416021, 562 | 14469841, 563 | 94536001, 564 | 798770161, 565 | 399001, 566 | 278545, 567 | 1200778753, 568 | 399906001, 569 | 1846817281, 570 | 231194965, 571 | 292776121, 572 | 221884001, 573 | 25603201, 574 | 507726901, 575 | 1894344001, 576 | 163954561, 577 | 955134181, 578 | 2573686441, 579 | 503758801, 580 | 2159003281, 581 | 124630273, 582 | 187188001, 583 | 2942952481, 584 | 930745621, 585 | 1899525601, 586 | 2560104001, 587 | 781347841, 588 | 935794081, 589 | 2201169601, 590 | 321602401, 591 | 851703301, 592 | 1223475841, 593 | 2361232477, 594 | 2278677961, 595 | 34901461, 596 | 102090781, 597 | 2436691321, 598 | 26932081, 599 | 45318561, 600 | 1200456577, 601 | 2224519921, 602 | 618068881, 603 | 1376844481, 604 | 2455921, 605 | 1784291041, 606 | 14676481, 607 | 2560600351, 608 | 2272748401, 609 | 1588247851, 610 | 299736181, 611 | 1404228421, 612 | 278152381, 613 | 703995733, 614 | 2407376665, 615 | 26474581, 616 | 1138049137, 617 | 270857521, 618 | 745864945, 619 | 334153, 620 | 41341321, 621 | 2766901501, 622 | 1760460481, 623 | 1018928485, 624 | 697906561, 625 | 75361, 626 | 393513121, 627 | 928482241, 628 | 357277921, 629 | 824389441, 630 | 1227220801, 631 | 2867755969, 632 | 1189238401, 633 | 2815304401, 634 | 1750412161, 635 | 1394746081, 636 | 2320690177, 637 | 1105, 638 | 2048443501, 639 | 9890881, 640 | 306871201, 641 | 3034203361, 642 | 707926801, 643 | 3030758401, 644 | 458368201, 645 | 50201089, 646 | 114910489, 647 | 963163201, 648 | 2943556201, 649 | 227752993, 650 | 1410833281, 651 | 93030145, 652 | 1534274841, 653 | 107714881, 654 | 9582145, 655 | 1227280681, 656 | 172430401, 657 | 547652161, 658 | 18162001, 659 | 2438403661, 660 | 1439328001, 661 | 1573132561, 662 | 193910977, 663 | 602074585, 664 | 109577161, 665 | 2064236401, 666 | 99861985, 667 | 7519441, 668 | 367939585, 669 | 20964961, 670 | 2064373921, 671 | 105309289, 672 | 1134044821, 673 | 743404663, 674 | 1696572001, 675 | 759472561, 676 | 1254318481, 677 | 825265, 678 | 1583582113, 679 | 7207201, 680 | 561481921, 681 | 1317828601, 682 | 1825568641, 683 | 3068534701, 684 | 1690230241, 685 | 53711113, 686 | 2342644921, 687 | 58489201, 688 | 33596641, 689 | 552894301, 690 | 45877861, 691 | 241242001, 692 | 186782401, 693 | 1090842145, 694 | 993420289, 695 | 96895441, 696 | 145124785, 697 | 670033, 698 | 311388337, 699 | 74165065, 700 | 27336673, 701 | 340561, 702 | 1024651, 703 | 6189121, 704 | 2574243721, 705 | 159492061, 706 | 611397865, 707 | 954732853, 708 | 128697361, 709 | 1001152801, 710 | 2519819281, 711 | 1194866101, 712 | 914801665, 713 | 2832480001, 714 | 2607162961, 715 | 40160737, 716 | 1420379065, 717 | 27402481, 718 | 765245881, 719 | 2998467901, 720 | 12262321, 721 | 968915521, 722 | 366532321, 723 | 2770560241, 724 | 410041, 725 | 1038165961, 726 | 1349671681, 727 | 1348964401, 728 | 1074363265, 729 | 2575260241, 730 | 413631505, 731 | 237597361, 732 | 2723859001, 733 | 852841, 734 | 348612265, 735 | 876850801, 736 | 104404861, 737 | 931694401, 738 | 393122521, 739 | 2391137281, 740 | 509033161, 741 | 188689501, 742 | 656187001, 743 | 1394942473, 744 | 1729, 745 | 2709611521, 746 | 616463809, 747 | 76595761, 748 | 132511681, 749 | 2215407601, 750 | 1419339691, 751 | 1841034961, 752 | 69331969, 753 | 302751505, 754 | 888700681, 755 | 1251992281, 756 | 2111488561, 757 | 15829633, 758 | 40622401, 759 | 2433601, 760 | 564651361, 761 | 1592668441, 762 | 111291181, 763 | 13992265, 764 | 1251295501, 765 | 686059921, 766 | 279377281, 767 | 403043257, 768 | 1330655041, 769 | 31405501, 770 | 39353665, 771 | 1507746241, 772 | 325546585, 773 | 6054985, 774 | 1896961801, 775 | 186393481, 776 | 329769721, 777 | 809702401, 778 | 981567505, 779 | 2602378721, 780 | 273769921, 781 | 540066241, 782 | 79624621, 783 | 72108421, 784 | 295826581, 785 | 79411201, 786 | 2480343553, 787 | 2239622113, 788 | 1260332137, 789 | 499310197, 790 | 490099681, 791 | 784966297, 792 | 413138881, 793 | 122785741, 794 | 1615335085, 795 | 556199281, 796 | 242641153, 797 | 32914441, 798 | 1626167341, 799 | 4767841, 800 | 29341, 801 | 153589801, 802 | 75151441, 803 | 5444489, 804 | 274569601, 805 | 868234081, 806 | 558570961, 807 | 1152271, 808 | 5031181, 809 | 1504651681, 810 | 21584305, 811 | 2858298301, 812 | 941056273, 813 | 1378483393, 814 | 64774081, 815 | 6049681, 816 | 1193221, 817 | 2230305949, 818 | 2199700321, 819 | 333065305, 820 | 577240273, 821 | 561777121, 822 | 2497638781, 823 | 292244833, 824 | 794937601, 825 | 48628801, 826 | 711374401, 827 | 55462177, 828 | 549333121, 829 | 748657, 830 | 1529544961, 831 | 5481451, 832 | 2073560401, 833 | 3001561441, 834 | 551672221, 835 | 1404111241, 836 | 958735681, 837 | 579606301, 838 | 852729121, 839 | 1050985, 840 | 1849811041, 841 | 834720601, 842 | 10585, 843 | 2456536681, 844 | 2029554241, 845 | 78091201, 846 | 2657502001, 847 | 162401, 848 | 4335241, 849 | 746706961, 850 | 226509361, 851 | 2457411265, 852 | 8719309, 853 | 3022354401, 854 | 1999004365, 855 | 855734401, 856 | 133205761, 857 | 67653433, 858 | 8830801, 859 | 1932608161, 860 | 438359041, 861 | 1428966001, 862 | 1079556193, 863 | 134809921, 864 | 2733494401, 865 | 1490522545, 866 | 1030401901, 867 | 10837321, 868 | 2000436751, 869 | 947993761, 870 | 35703361, 871 | 1407548341, 872 | 727083001, 873 | 625060801, 874 | 172947529, 875 | 17316001, 876 | 13187665, 877 | 121247281, 878 | 105869401, 879 | 119327041, 880 | 10267951, 881 | 1803278401, 882 | 1312114945, 883 | 1657700353, 884 | 255160621, 885 | 2117725921, 886 | 36121345, 887 | 713588401, 888 | 3057601, 889 | 775368901, 890 | 775866001, 891 | 2323147201, 892 | 1031750401, 893 | 11972017, 894 | 238244041, 895 | 963168193, 896 | 2787998641, 897 | 12490201, 898 | 84311569, 899 | 43620409, 900 | 481239361, 901 | 1439492041, 902 | 126217, 903 | 257495641, 904 | 368113411, 905 | 722923201, 906 | 1210178305, 907 | 1943951041, 908 | 471905281, 909 | 180115489, 910 | 19683001, 911 | 1962804565, 912 | 104569501, 913 | 990893569, 914 | 776176261, 915 | 15841, 916 | 2677147201, 917 | 321197185, 918 | 1752710401, 919 | 958970545, 920 | 863984881, 921 | 1070659201, 922 | 1597009393, 923 | 1976295241, 924 | 612816751, 925 | 600892993, 926 | 2217951073, 927 | 158404141, 928 | 2630374741, 929 | 196358977, 930 | 27062101, 931 | 934784929, 932 | 3072094201, 933 | 43286881, 934 | 101957401, 935 | 105117481, 936 | 2126689501, 937 | 170947105, 938 | 150846961, 939 | 56052361, 940 | 2523947041, 941 | 829678141, 942 | 93614521, 943 | 2842912381, 944 | 510825601, 945 | 10402561, 946 | 940123801, 947 | 4909177, 948 | 1833328621, 949 | 471441001, 950 | 68154001, 951 | 1316958721, 952 | 2353639681, 953 | 597717121, 954 | 78120001, 955 | 382304161, 956 | 1950276565, 957 | 252141121, 958 | 2097317377, 959 | 40430401, 960 | 15403285, 961 | 1490078305, 962 | 790020001, 963 | 366652201, 964 | 2665141921, 965 | 593234929, 966 | 1672719217, 967 | 80927821, 968 | 81926461, 969 | 1131222841, 970 | 549117205, 971 | 1701016801, 972 | 6733693, 973 | 2494465921, 974 | 621101185, 975 | 1678569121, 976 | 364590721, 977 | 2510085721, 978 | 214852609, 979 | 986088961, 980 | 1422477001, 981 | 289860481, 982 | 100427041, 983 | 3077802001, 984 | 318266641, 985 | 358940737, 986 | 90698401, 987 | 652969351, 988 | 2377166401, 989 | 1854001513, 990 | 2685422593, 991 | 67371265, 992 | 173085121, 993 | 416964241, 994 | 42490801, 995 | 40917241, 996 | 2313774001, 997 | 17812081, 998 | 48321001, 999 | 1257102001, 1000 | 1857241, 1001 | 296559361, 1002 | 530443201, 1003 | 2223876601, 1004 | 1332521065, 1005 | 65037817, 1006 | 333229141, 1007 | 29020321, 1008 | 178837201, 1009 | 1858395529, 1010 | 2113921, 1011 | 2023528501, 1012 | 1646426881, 1013 | 656601, 1014 | 289766701, 1015 | 354938221, 1016 | 549538081, 1017 | 752102401, 1018 | 573896881, 1019 | 1177800481, 1020 | 5310721, 1021 | 512461, 1022 | 2295209281, 1023 | 1452767521, 1024 | 5632705, 1025 | 175997185, 1026 | 3146221, 1027 | 176659201, 1028 | 28787185, 1029 | 2309027281, 1030 | 83966401, 1031 | 1337805505, 1032 | 1295577361, 1033 | 832060801, 1034 | 295643089, 1035 | 1769031901, 1036 | 2776874941, 1037 | 405739681, 1038 | 3069196417, 1039 | 1256855041, 1040 | 2702470861, 1041 | 897880321, 1042 | 542497201, 1043 | 985052881, 1044 | 771043201, 1045 | 429553345, 1046 | 2430556381, 1047 | 2335640077, 1048 | 885336481, 1049 | 1688214529, 1050 | 115542505, 1051 | 174352641, 1052 | 275283401, 1053 | 72286501, 1054 | 1072570801, 1055 | 46657, 1056 | 33302401, 1057 | 3664585, 1058 | 1515785041, 1059 | 62745, 1060 | ] 1061 | for n in numList: 1062 | self.assertEqual(prime_fact.is_probable_prime(n), False) 1063 | 1064 | def is_prime_true_case_18_dig(self): 1065 | numList = [ 1066 | 100000000000000003, 1067 | 100000000000000013, 1068 | 100000000000000019, 1069 | 100000000000000021, 1070 | 100000000000000049, 1071 | 100000000000000081, 1072 | 100000000000000099, 1073 | 100000000000000141, 1074 | 100000000000000181, 1075 | 100000000000000337, 1076 | 100000000000000339, 1077 | 100000000000000369, 1078 | 100000000000000379, 1079 | 100000000000000423, 1080 | 100000000000000519, 1081 | 100000000000000543, 1082 | 100000000000000589, 1083 | 100000000000000591, 1084 | 100000000000000609, 1085 | 100000000000000669, 1086 | 100000000000000691, 1087 | 100000000000000781, 1088 | 100000000000000787, 1089 | 100000000000000817, 1090 | 100000000000000819, 1091 | 100000000000000871, 1092 | 100000000000000889, 1093 | 100000000000001053, 1094 | 100000000000001071, 1095 | 100000000000001093, 1096 | 100000000000001099, 1097 | 100000000000001107, 1098 | 100000000000001201, 1099 | 100000000000001219, 1100 | 100000000000001221, 1101 | 100000000000001243, 1102 | 100000000000001249, 1103 | 100000000000001287, 1104 | 100000000000001293, 1105 | 100000000000001327, 1106 | 100000000000001399, 1107 | 100000000000001429, 1108 | 100000000000001449, 1109 | 100000000000001459, 1110 | 100000000000001573, 1111 | 100000000000001603, 1112 | 100000000000001623, 1113 | 100000000000001641, 1114 | 100000000000001903, 1115 | 100000000000001921, 1116 | 100000000000001947, 1117 | 100000000000001971, 1118 | 100000000000001989, 1119 | 100000000000002029, 1120 | 100000000000002041, 1121 | 100000000000002139, 1122 | 100000000000002169, 1123 | 100000000000002197, 1124 | 100000000000002203, 1125 | 100000000000002307, 1126 | 100000000000002341, 1127 | 100000000000002409, 1128 | 100000000000002433, 1129 | 100000000000002461, 1130 | 100000000000002493, 1131 | 100000000000002569, 1132 | 100000000000002587, 1133 | 100000000000002589, 1134 | 100000000000002643, 1135 | 100000000000002649, 1136 | 100000000000002701, 1137 | 100000000000002709, 1138 | 100000000000002733, 1139 | 100000000000002737, 1140 | 100000000000002799, 1141 | 100000000000002821, 1142 | 100000000000002833, 1143 | 100000000000002857, 1144 | 100000000000002859, 1145 | 100000000000002911, 1146 | 100000000000002913, 1147 | 100000000000002929, 1148 | 100000000000002937, 1149 | 100000000000002959, 1150 | 100000000000003007, 1151 | 100000000000003021, 1152 | 100000000000003039, 1153 | 100000000000003079, 1154 | 100000000000003177, 1155 | 100000000000003253, 1156 | 100000000000003289, 1157 | 100000000000003307, 1158 | 100000000000003417, 1159 | 100000000000003447, 1160 | 100000000000003457, 1161 | 100000000000003487, 1162 | 100000000000003559, 1163 | 100000000000003561, 1164 | 100000000000003699, 1165 | 100000000000003777, 1166 | ] 1167 | for n in numList: 1168 | self.assertEqual(prime_fact.is_probable_prime(n), True) 1169 | 1170 | def is_prime_sieve_eratosthenes(self): 1171 | MAX_N = 2000 1172 | sieveList = [True for i in range(MAX_N)] 1173 | sieveList[0] = False 1174 | sieveList[1] = False 1175 | for i in range(2, MAX_N): 1176 | if sieveList[i]: 1177 | for j in range(2 * i, MAX_N, i): 1178 | sieveList[j] = False 1179 | for i in range(MAX_N): 1180 | self.assertEqual(prime_fact.is_probable_prime(i), sieveList[i]) 1181 | 1182 | def test_is_prime(self): 1183 | self.is_prime_example() 1184 | self.is_prime_hackissue1325() 1185 | self.is_prime_carmichael() 1186 | self.is_prime_true_case_18_dig() 1187 | self.is_prime_sieve_eratosthenes() 1188 | 1189 | def test_divisors(self): 1190 | """Test divisor enumeration""" 1191 | # TODO: Add test cases for divisor enumeration 1192 | pass 1193 | 1194 | def test_euler_phi(self): 1195 | """Test Euler's totient function""" 1196 | # TODO: Add test cases for Euler's totient function 1197 | pass 1198 | 1199 | def test_prime_factorization_small_numbers(self): 1200 | """Test prime factorization of small numbers""" 1201 | # Test small primes 1202 | self.assertEqual(dict(prime_fact.prime_fact(2)), {2: 1}) 1203 | self.assertEqual(dict(prime_fact.prime_fact(3)), {3: 1}) 1204 | self.assertEqual(dict(prime_fact.prime_fact(5)), {5: 1}) 1205 | 1206 | # Test composite numbers 1207 | self.assertEqual(dict(prime_fact.prime_fact(6)), {2: 1, 3: 1}) 1208 | self.assertEqual(dict(prime_fact.prime_fact(12)), {2: 2, 3: 1}) 1209 | self.assertEqual(dict(prime_fact.prime_fact(18)), {2: 1, 3: 2}) 1210 | 1211 | def test_prime_factorization_edge_cases(self): 1212 | """Test edge cases for prime factorization""" 1213 | # Test 1 1214 | self.assertEqual(dict(prime_fact.prime_fact(1)), {}) 1215 | 1216 | # Test powers of 2 1217 | self.assertEqual(dict(prime_fact.prime_fact(8)), {2: 3}) 1218 | self.assertEqual(dict(prime_fact.prime_fact(16)), {2: 4}) 1219 | 1220 | # Test squares of primes 1221 | self.assertEqual(dict(prime_fact.prime_fact(49)), {7: 2}) 1222 | self.assertEqual(dict(prime_fact.prime_fact(121)), {11: 2}) 1223 | 1224 | def test_is_probable_prime(self): 1225 | """Test primality testing""" 1226 | # Test known primes 1227 | known_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] 1228 | for p in known_primes: 1229 | self.assertTrue(prime_fact.is_probable_prime(p)) 1230 | 1231 | # Test known composites 1232 | known_composites = [4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20] 1233 | for c in known_composites: 1234 | self.assertFalse(prime_fact.is_probable_prime(c)) 1235 | 1236 | # Test edge cases 1237 | self.assertFalse(prime_fact.is_probable_prime(1)) 1238 | self.assertTrue(prime_fact.is_probable_prime(2)) 1239 | 1240 | def test_divisors_basic(self): 1241 | """Test basic divisor enumeration""" 1242 | # Test 1 1243 | divisors_1 = sorted(prime_fact.divisors(1)) 1244 | self.assertEqual(divisors_1, [1]) 1245 | 1246 | # Test 6 = 2 * 3 1247 | divisors_6 = sorted(prime_fact.divisors(6)) 1248 | self.assertEqual(divisors_6, [1, 2, 3, 6]) 1249 | 1250 | # Test 12 = 2^2 * 3 1251 | divisors_12 = sorted(prime_fact.divisors(12)) 1252 | self.assertEqual(divisors_12, [1, 2, 3, 4, 6, 12]) 1253 | 1254 | def test_divisors_prime_powers(self): 1255 | """Test divisors of prime powers""" 1256 | # Test 8 = 2^3 1257 | divisors_8 = sorted(prime_fact.divisors(8)) 1258 | self.assertEqual(divisors_8, [1, 2, 4, 8]) 1259 | 1260 | # Test 9 = 3^2 1261 | divisors_9 = sorted(prime_fact.divisors(9)) 1262 | self.assertEqual(divisors_9, [1, 3, 9]) 1263 | 1264 | def test_totient_basic(self): 1265 | """Test Euler's totient function""" 1266 | # φ(1) = 1 1267 | self.assertEqual(prime_fact.totient(1), 1) 1268 | 1269 | # φ(p) = p-1 for prime p 1270 | self.assertEqual(prime_fact.totient(2), 1) 1271 | self.assertEqual(prime_fact.totient(3), 2) 1272 | self.assertEqual(prime_fact.totient(5), 4) 1273 | self.assertEqual(prime_fact.totient(7), 6) 1274 | 1275 | # φ(6) = φ(2*3) = 6*(1-1/2)*(1-1/3) = 2 1276 | self.assertEqual(prime_fact.totient(6), 2) 1277 | 1278 | # φ(12) = φ(2^2*3) = 12*(1-1/2)*(1-1/3) = 4 1279 | self.assertEqual(prime_fact.totient(12), 4) 1280 | 1281 | def test_lcm_basic(self): 1282 | """Test least common multiple""" 1283 | # lcm(6, 8) = 24 1284 | self.assertEqual(prime_fact.lcm(6, 8), 24) 1285 | 1286 | # lcm(12, 18) = 36 1287 | self.assertEqual(prime_fact.lcm(12, 18), 36) 1288 | 1289 | # lcm with 1 1290 | self.assertEqual(prime_fact.lcm(1, 5), 5) 1291 | self.assertEqual(prime_fact.lcm(7, 1), 7) 1292 | 1293 | # lcm of coprime numbers 1294 | self.assertEqual(prime_fact.lcm(3, 5), 15) 1295 | self.assertEqual(prime_fact.lcm(7, 11), 77) 1296 | 1297 | def test_larger_numbers(self): 1298 | """Test with larger numbers""" 1299 | # Test a larger composite number 1300 | pf_100 = dict(prime_fact.prime_fact(100)) 1301 | self.assertEqual(pf_100, {2: 2, 5: 2}) 1302 | 1303 | # Test totient of 100 1304 | self.assertEqual(prime_fact.totient(100), 40) 1305 | 1306 | # Test number of divisors of 100 1307 | divisors_100 = prime_fact.divisors(100) 1308 | self.assertEqual(len(divisors_100), 9) # (2+1)*(2+1) = 9 1309 | 1310 | def test_mathematical_properties(self): 1311 | """Test mathematical properties""" 1312 | # Test that all prime factors actually divide the number 1313 | for n in [60, 72, 90]: 1314 | factors = prime_fact.prime_fact(n) 1315 | for p, e in factors.items(): 1316 | self.assertEqual(n % p, 0) 1317 | self.assertGreater(e, 0) 1318 | 1319 | # Test that divisors actually divide the number 1320 | for n in [24, 30, 36]: 1321 | divs = prime_fact.divisors(n) 1322 | for d in divs: 1323 | self.assertEqual(n % d, 0) 1324 | 1325 | # Test lcm property: lcm(a,b) * gcd(a,b) = a * b 1326 | import math 1327 | 1328 | test_pairs = [(12, 18), (15, 20), (7, 11)] 1329 | for a, b in test_pairs: 1330 | self.assertEqual(prime_fact.lcm(a, b) * math.gcd(a, b), a * b) 1331 | 1332 | def test_consistency_checks(self): 1333 | """Test consistency between different functions""" 1334 | # Test that prime factorization reconstructs the original number 1335 | test_numbers = [24, 36, 48, 60] 1336 | for n in test_numbers: 1337 | factors = prime_fact.prime_fact(n) 1338 | reconstructed = 1 1339 | for p, e in factors.items(): 1340 | reconstructed *= p**e 1341 | self.assertEqual(reconstructed, n) 1342 | 1343 | 1344 | if __name__ == "__main__": 1345 | unittest.main() 1346 | --------------------------------------------------------------------------------