[
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish to PyPI\n\non:\n  release:\n    types: [published]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4\n\n      - name: Set up Python\n        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065  # v5\n        with:\n          python-version: \"3.13\"\n\n      - name: Install build tools\n        run: python -m pip install --upgrade pip build\n\n      - name: Build package\n        run: python -m build\n\n      - name: Upload build artifacts\n        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08  # v4\n        with:\n          name: dist\n          path: dist/\n\n  publish:\n    needs: build\n    runs-on: ubuntu-latest\n    environment: pypi\n    permissions:\n      id-token: write\n    steps:\n      - name: Download build artifacts\n        uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16  # v4\n        with:\n          name: dist\n          path: dist/\n\n      - name: Publish to PyPI\n        uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e  # release/v1\n"
  },
  {
    "path": ".github/workflows/python-app.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches: [master, main]\n  pull_request:\n    branches: [master, main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v5\n        with:\n          python-version: ${{ matrix.python-version }}\n\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          pip install -e \".[dev]\"\n\n      - name: Lint with ruff\n        run: ruff check algorithms/ tests/\n\n      - name: Test with pytest\n        run: python -m pytest\n"
  },
  {
    "path": ".gitignore",
    "content": "__pycache__/\n*.py[cod]\n*.iml\n*.xml\n.idea/\n.cache/\n.pytest_cache/\n.coverage\n# Setuptools distribution folder.\n/dist/\n# Python egg metadata, regenerated from source files by setuptools.\n/*.egg-info\n/*.egg\n# docs\nbuild/\npythonenv3.8/\n.vscode/\n# Ignoring the virtual Environment when using GitHub Codespaces\n.venv/"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kwk236@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe love pull requests from everyone. By contributing to this repository, you\nagree to abide by the [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## Get Started  \n\n* First [fork][fork] the repository and then clone it using:\n\n    git clone git@github.com:your-username/algorithms.git  \n\n* After that create a branch for your changes. For example:  \n  * add_XXX if you will add new algorithms or data structures.  \n  * fix_XXX if you will fix a bug on a certain algorithm or data structure.  \n  * test_XXX if you wrote a test/s.  \n  * doc_XXX if you added to or edited documentation.\n\nYou may contribute by:\n- implementing new algorithms in the repo. Be sure to keep it under\nright section (e.g. [array](array), [dp](dp), etc). Make a new section for it if\nit doesn't fall under any section. Make sure that your implementation works.  \n- optimizing or improving the existing algorithms.\n- adding a different solution for the problem.\n- finding and fixing bugs.\n- adding examples to explain the algorithms better.\n- adding test cases.\n- improving documentation.\n\n## Pull Requests\nPush to your fork and [submit a pull request][pr].\n\nWe will review and may suggest some changes or improvements or alternatives.\nSome things that will increase the chance that your pull request is accepted:\n\n* All algorithms should be written in **Python 3**.\n(There are a few algorithms still in _Python 2_ syntax. You can start by converting\n[those][issue120] to _Python 3_.)\n* Write clean and understandable code.\n* Properly comment the code and briefly explain what the algorithm is doing in the [docstrings][docstr].\n* You may also explain the output using a minimal example.\n* Try to also include a couple of test cases for the algorithm.\n* Write a [good commit message][commit].\n\n\n## Issues\nSubmit a [new issue][newissue] if there is an algorithm to add, or if a bug was found in an existing algorithm. Before submitting a new issue please review the [existing issues][issues] to avoid creating duplicates. Also, consider resolving current issues or contributing to the discussion on an issue.\n\n## Collaborators\nYou can ask for any help or clarifications from the collaborators.  \n[Keon Kim](https://github.com/keon)\n\n[Rahul Goswami](https://github.com/goswami-rahul)\n\n[Ankit Agarwal](https://github.com/ankit167)\n\n[Hai Hoang Dang](https://github.com/danghai)\n\n[Saad](https://github.com/SaadBenn)\n\n[fork]: https://help.github.com/articles/fork-a-repo/\n[docstr]: https://www.python.org/dev/peps/pep-0257/#multi-line-docstrings\n[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n[pr]: https://github.com/keon/algorithms/compare/\n[newissue]: https://github.com/keon/algorithms/issues/new\n[issue120]: https://github.com/keon/algorithms/issues/120\n[issues]: https://github.com/keon/algorithms/issues/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Keon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.md\ninclude LICENSE\ninclude algorithms/*\n"
  },
  {
    "path": "README.md",
    "content": "[![PyPI version](https://badge.fury.io/py/algorithms.svg)](https://badge.fury.io/py/algorithms)\n[![Open Source Helpers](https://www.codetriage.com/keon/algorithms/badges/users.svg)](https://www.codetriage.com/keon/algorithms)\n\n# algorithms\n\nMinimal, clean, and well-documented implementations of data structures and algorithms in Python 3.\n\nEach file is self-contained with docstrings, type hints, and complexity notes &mdash; designed to be read and learned from.\n\n## Quick Start\n\n### Install\n\n```bash\npip install algorithms\n```\n\n### Use\n\n```python\nfrom algorithms.sorting import merge_sort\n\nprint(merge_sort([38, 27, 43, 3, 9, 82, 10]))\n# [3, 9, 10, 27, 38, 43, 82]\n```\n\n```python\nfrom algorithms.data_structures import BinaryHeap, Trie, BST\nfrom algorithms.graph import dijkstra, bellman_ford\nfrom algorithms.tree import TreeNode\n```\n\n### Examples\n\n**Graph &mdash; Dijkstra's shortest path:**\n\n```python\nfrom algorithms.graph import dijkstra\n\ngraph = {\n    \"s\": {\"a\": 2, \"b\": 1},\n    \"a\": {\"s\": 3, \"b\": 4, \"c\": 8},\n    \"b\": {\"s\": 4, \"a\": 2, \"d\": 2},\n    \"c\": {\"a\": 2, \"d\": 7, \"t\": 4},\n    \"d\": {\"b\": 1, \"c\": 11, \"t\": 5},\n    \"t\": {\"c\": 3, \"d\": 5},\n}\nprint(dijkstra(graph, \"s\", \"t\"))\n# (8, ['s', 'b', 'd', 't'])\n```\n\n**Dynamic programming &mdash; coin change:**\n\n```python\nfrom algorithms.dynamic_programming import count\n\n# Number of ways to make amount 10 using denominations [2, 5, 3, 6]\nprint(count([2, 5, 3, 6], 10))\n# 5\n```\n\n**Backtracking &mdash; generate permutations:**\n\n```python\nfrom algorithms.backtracking import permute\n\nprint(permute([1, 2, 3]))\n# [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]\n```\n\n**Data structures &mdash; binary heap:**\n\n```python\nfrom algorithms.data_structures import BinaryHeap\n\nheap = BinaryHeap()\nfor val in [5, 3, 8, 1, 9]:\n    heap.insert(val)\nprint(heap.remove_min())  # 1\nprint(heap.remove_min())  # 3\n```\n\n**Searching &mdash; binary search:**\n\n```python\nfrom algorithms.searching import binary_search\n\nprint(binary_search([1, 3, 5, 7, 9, 11], 7))\n# 3   (index of target)\n```\n\n**Tree &mdash; inorder traversal:**\n\n```python\nfrom algorithms.tree import TreeNode\nfrom algorithms.tree import inorder\n\nroot = TreeNode(4)\nroot.left = TreeNode(2)\nroot.right = TreeNode(6)\nroot.left.left = TreeNode(1)\nroot.left.right = TreeNode(3)\n\nprint(inorder(root))\n# [1, 2, 3, 4, 6]\n```\n\n**String &mdash; Knuth-Morris-Pratt pattern matching:**\n\n```python\nfrom algorithms.string import knuth_morris_pratt\n\nprint(knuth_morris_pratt(\"abxabcabcaby\", \"abcaby\"))\n# 6   (starting index of match)\n```\n\n### Run Tests\n\n```bash\npython -m pytest tests/\n```\n\n## Project Structure\n\n```\nalgorithms/\n    data_structures/     # Reusable data structure implementations\n    array/               # Array manipulation algorithms\n    backtracking/        # Constraint satisfaction & enumeration\n    bit_manipulation/    # Bitwise operations & tricks\n    compression/         # Encoding & compression schemes\n    dynamic_programming/ # Optimal substructure & memoization\n    graph/               # Graph algorithms (BFS, DFS, shortest path, flow, ...)\n    greedy/              # Greedy strategies\n    heap/                # Heap-based algorithms\n    linked_list/         # Linked list algorithms\n    map/                 # Hash-map-based algorithms\n    math/                # Number theory, combinatorics, algebra\n    matrix/              # 2D array & linear algebra operations\n    queue/               # Queue-based algorithms\n    searching/           # Search algorithms (binary, linear, ...)\n    set/                 # Set-based algorithms\n    sorting/             # Sorting algorithms\n    stack/               # Stack-based algorithms\n    streaming/           # Streaming & sketching algorithms\n    string/              # String matching, manipulation, parsing\n    tree/                # Tree algorithms (traversal, BST ops, ...)\ntests/                   # One test file per topic\n```\n\n## Data Structures\n\nAll core data structures live in [`algorithms/data_structures/`](algorithms/data_structures):\n\n| Data Structure | Module | Key Classes |\n|---|---|---|\n| AVL Tree | `avl_tree.py` | `AvlTree` |\n| B-Tree | `b_tree.py` | `BTree` |\n| Binary Search Tree | `bst.py` | `BST` |\n| Fenwick Tree | `fenwick_tree.py` | `Fenwick_Tree` |\n| Graph | `graph.py` | `Node`, `DirectedEdge`, `DirectedGraph` |\n| Hash Table | `hash_table.py` | `HashTable`, `ResizableHashTable` |\n| Heap | `heap.py` | `BinaryHeap` |\n| KD Tree | `kd_tree.py` | `KDTree` |\n| Linked List | `linked_list.py` | `SinglyLinkedListNode`, `DoublyLinkedListNode` |\n| Priority Queue | `priority_queue.py` | `PriorityQueue` |\n| Queue | `queue.py` | `ArrayQueue`, `LinkedListQueue` |\n| Red-Black Tree | `red_black_tree.py` | `RBTree` |\n| Segment Tree | `segment_tree.py`, `iterative_segment_tree.py` | `SegmentTree` |\n| Separate Chaining Hash Table | `separate_chaining_hash_table.py` | `SeparateChainingHashTable` |\n| Sqrt Decomposition | `sqrt_decomposition.py` | `SqrtDecomposition` |\n| Stack | `stack.py` | `ArrayStack`, `LinkedListStack` |\n| Trie | `trie.py` | `Trie` |\n| Union-Find | `union_find.py` | `Union` |\n| vEB Tree | `veb_tree.py` | `VEBTree` |\n\n## Algorithms\n\n### Array\n\n- [delete_nth](algorithms/array/delete_nth.py) &mdash; keep at most N occurrences of each element\n- [flatten](algorithms/array/flatten.py) &mdash; recursively flatten nested arrays into a single list\n- [garage](algorithms/array/garage.py) &mdash; minimum swaps to rearrange a parking lot\n- [josephus](algorithms/array/josephus.py) &mdash; eliminate every k-th person in a circular arrangement\n- [limit](algorithms/array/limit.py) &mdash; filter elements within min/max bounds\n- [longest_non_repeat](algorithms/array/longest_non_repeat.py) &mdash; longest substring without repeating characters\n- [max_ones_index](algorithms/array/max_ones_index.py) &mdash; find the zero to flip for the longest run of ones\n- [merge_intervals](algorithms/array/merge_intervals.py) &mdash; combine overlapping intervals\n- [missing_ranges](algorithms/array/missing_ranges.py) &mdash; find gaps between a low and high bound\n- [move_zeros](algorithms/array/move_zeros.py) &mdash; move all zeros to the end, preserving order\n- [n_sum](algorithms/array/n_sum.py) &mdash; find all unique n-tuples that sum to a target\n- [plus_one](algorithms/array/plus_one.py) &mdash; add one to a number represented as a digit array\n- [remove_duplicates](algorithms/array/remove_duplicates.py) &mdash; remove duplicate elements preserving order\n- [rotate](algorithms/array/rotate.py) &mdash; rotate an array right by k positions\n- [summarize_ranges](algorithms/array/summarize_ranges.py) &mdash; summarize consecutive integers as range tuples\n- [three_sum](algorithms/array/three_sum.py) &mdash; find all unique triplets that sum to zero\n- [top_1](algorithms/array/top_1.py) &mdash; find the most frequently occurring values\n- [trimmean](algorithms/array/trimmean.py) &mdash; compute mean after trimming extreme values\n- [two_sum](algorithms/array/two_sum.py) &mdash; find two indices whose values sum to a target\n\n### Backtracking\n\n- [add_operators](algorithms/backtracking/add_operators.py) &mdash; insert +, -, * between digits to reach a target\n- [anagram](algorithms/backtracking/anagram.py) &mdash; check if two strings are anagrams\n- [array_sum_combinations](algorithms/backtracking/array_sum_combinations.py) &mdash; find three-element combos from arrays that hit a target sum\n- [combination_sum](algorithms/backtracking/combination_sum.py) &mdash; find combinations (with reuse) that sum to a target\n- [factor_combinations](algorithms/backtracking/factor_combinations.py) &mdash; generate all factor combinations of a number\n- [find_words](algorithms/backtracking/find_words.py) &mdash; find words on a letter board via trie-based search\n- [generate_abbreviations](algorithms/backtracking/generate_abbreviations.py) &mdash; generate all possible abbreviations of a word\n- [generate_parenthesis](algorithms/backtracking/generate_parenthesis.py) &mdash; generate all valid parenthesis combinations\n- [letter_combination](algorithms/backtracking/letter_combination.py) &mdash; phone keypad digit-to-letter combinations\n- [palindrome_partitioning](algorithms/backtracking/palindrome_partitioning.py) &mdash; partition a string into palindromic substrings\n- [pattern_match](algorithms/backtracking/pattern_match.py) &mdash; match a string to a pattern via bijection mapping\n- [permute](algorithms/backtracking/permute.py) &mdash; generate all permutations of distinct elements\n- [permute_unique](algorithms/backtracking/permute_unique.py) &mdash; generate unique permutations when duplicates exist\n- [subsets](algorithms/backtracking/subsets.py) &mdash; generate all subsets (power set)\n- [minimax](algorithms/backtracking/minimax.py) &mdash; game-tree search with alpha-beta pruning\n- [subsets_unique](algorithms/backtracking/subsets_unique.py) &mdash; generate unique subsets when duplicates exist\n\n### Bit Manipulation\n\n- [add_bitwise_operator](algorithms/bit_manipulation/add_bitwise_operator.py) &mdash; add two integers using only bitwise operations\n- [binary_gap](algorithms/bit_manipulation/binary_gap.py) &mdash; longest distance between consecutive 1-bits\n- [bit_operation](algorithms/bit_manipulation/bit_operation.py) &mdash; get, set, clear, and update individual bits\n- [bytes_int_conversion](algorithms/bit_manipulation/bytes_int_conversion.py) &mdash; convert between integers and byte sequences\n- [count_flips_to_convert](algorithms/bit_manipulation/count_flips_to_convert.py) &mdash; count bit flips needed to convert one integer to another\n- [count_ones](algorithms/bit_manipulation/count_ones.py) &mdash; count the number of 1-bits (Hamming weight)\n- [find_difference](algorithms/bit_manipulation/find_difference.py) &mdash; find the added character between two strings using XOR\n- [find_missing_number](algorithms/bit_manipulation/find_missing_number.py) &mdash; find a missing number in a sequence using XOR\n- [flip_bit_longest_sequence](algorithms/bit_manipulation/flip_bit_longest_sequence.py) &mdash; longest run of 1s after flipping a single 0\n- [gray_code](algorithms/bit_manipulation/gray_code.py) &mdash; generate Gray code sequences and convert between Gray and binary\n- [has_alternative_bit](algorithms/bit_manipulation/has_alternative_bit.py) &mdash; check if binary representation has alternating bits\n- [insert_bit](algorithms/bit_manipulation/insert_bit.py) &mdash; insert bits at a specific position in an integer\n- [power_of_two](algorithms/bit_manipulation/power_of_two.py) &mdash; check if an integer is a power of two\n- [remove_bit](algorithms/bit_manipulation/remove_bit.py) &mdash; remove a bit at a given position\n- [reverse_bits](algorithms/bit_manipulation/reverse_bits.py) &mdash; reverse all 32 bits of an unsigned integer\n- [single_number](algorithms/bit_manipulation/single_number.py) &mdash; find the element appearing once (others appear twice) via XOR\n- [single_number2](algorithms/bit_manipulation/single_number2.py) &mdash; find the element appearing once (others appear three times)\n- [single_number3](algorithms/bit_manipulation/single_number3.py) &mdash; find two unique elements (others appear twice)\n- [subsets](algorithms/bit_manipulation/subsets.py) &mdash; generate all subsets using bitmask enumeration\n- [swap_pair](algorithms/bit_manipulation/swap_pair.py) &mdash; swap adjacent bit pairs in an integer\n\n### Compression\n\n- [elias](algorithms/compression/elias.py) &mdash; Elias gamma and delta universal integer coding\n- [huffman_coding](algorithms/compression/huffman_coding.py) &mdash; variable-length prefix codes for lossless compression\n- [rle_compression](algorithms/compression/rle_compression.py) &mdash; run-length encoding for consecutive character compression\n\n### Dynamic Programming\n\n- [bitmask](algorithms/dynamic_programming/bitmask.py) &mdash; travelling salesman problem via bitmask dynamic programming\n- [buy_sell_stock](algorithms/dynamic_programming/buy_sell_stock.py) &mdash; maximize profit from a stock price array\n- [climbing_stairs](algorithms/dynamic_programming/climbing_stairs.py) &mdash; count ways to climb stairs taking 1 or 2 steps\n- [coin_change](algorithms/dynamic_programming/coin_change.py) &mdash; minimum coins to make a given amount\n- [combination_sum](algorithms/dynamic_programming/combination_sum.py) &mdash; count combinations that sum to a target (with reuse)\n- [count_paths_dp](algorithms/dynamic_programming/count_paths_dp.py) &mdash; count paths in a grid using recursion, memoization, and bottom-up DP\n- [edit_distance](algorithms/dynamic_programming/edit_distance.py) &mdash; minimum edits to transform one string into another\n- [egg_drop](algorithms/dynamic_programming/egg_drop.py) &mdash; minimize trials to find the critical floor\n- [fibonacci](algorithms/dynamic_programming/fib.py) &mdash; compute Fibonacci numbers with memoization\n- [hosoya_triangle](algorithms/dynamic_programming/hosoya_triangle.py) &mdash; generate the Hosoya triangle of Fibonacci-like numbers\n- [house_robber](algorithms/dynamic_programming/house_robber.py) &mdash; maximize loot from non-adjacent houses\n- [int_divide](algorithms/dynamic_programming/int_divide.py) &mdash; count the number of integer partitions\n- [job_scheduling](algorithms/dynamic_programming/job_scheduling.py) &mdash; maximize profit from weighted job scheduling\n- [k_factor](algorithms/dynamic_programming/k_factor.py) &mdash; find the k-factor of a string pattern\n- [knapsack](algorithms/dynamic_programming/knapsack.py) &mdash; maximize value under a weight constraint\n- [longest_common_subsequence](algorithms/dynamic_programming/longest_common_subsequence.py) &mdash; find the longest common subsequence of two strings\n- [longest_increasing](algorithms/dynamic_programming/longest_increasing.py) &mdash; find the longest increasing subsequence\n- [matrix_chain_order](algorithms/dynamic_programming/matrix_chain_order.py) &mdash; minimize scalar multiplications for matrix chain\n- [max_product_subarray](algorithms/dynamic_programming/max_product_subarray.py) &mdash; find the contiguous subarray with maximum product\n- [max_subarray](algorithms/dynamic_programming/max_subarray.py) &mdash; maximum sum subarray (Kadane's algorithm)\n- [min_cost_path](algorithms/dynamic_programming/min_cost_path.py) &mdash; minimum-cost path through a grid\n- [num_decodings](algorithms/dynamic_programming/num_decodings.py) &mdash; count ways to decode a digit string into letters\n- [planting_trees](algorithms/dynamic_programming/planting_trees.py) &mdash; optimize tree planting for maximum profit\n- [regex_matching](algorithms/dynamic_programming/regex_matching.py) &mdash; match a string against a pattern with `.` and `*` wildcards\n- [rod_cut](algorithms/dynamic_programming/rod_cut.py) &mdash; maximize revenue from cutting a rod into pieces\n- [word_break](algorithms/dynamic_programming/word_break.py) &mdash; check if a string can be segmented into dictionary words\n\n### Graph\n\n- [a_star](algorithms/graph/a_star.py) &mdash; heuristic shortest-path search (A* algorithm)\n- [all_factors](algorithms/graph/all_factors.py) &mdash; find all factor combinations of a number\n- [all_pairs_shortest_path](algorithms/graph/all_pairs_shortest_path.py) &mdash; Floyd-Warshall all-pairs shortest paths\n- [bellman_ford](algorithms/graph/bellman_ford.py) &mdash; single-source shortest path with negative edge weights\n- [blossom](algorithms/graph/blossom.py) &mdash; Edmonds' blossom algorithm for maximum matching in general graphs\n- [check_bipartite](algorithms/graph/check_bipartite.py) &mdash; determine if a graph is two-colorable\n- [check_digraph_strongly_connected](algorithms/graph/check_digraph_strongly_connected.py) &mdash; check if a directed graph is strongly connected\n- [clone_graph](algorithms/graph/clone_graph.py) &mdash; deep-copy an undirected graph\n- [count_connected_number_of_component](algorithms/graph/count_connected_number_of_component.py) &mdash; count connected components in an undirected graph\n- [count_islands (BFS)](algorithms/graph/count_islands_bfs.py) &mdash; count islands in a grid using breadth-first search\n- [count_islands (DFS)](algorithms/graph/count_islands_dfs.py) &mdash; count islands in a grid using depth-first search\n- [count_islands (Union-Find)](algorithms/graph/count_islands_unionfind.py) &mdash; count islands using a disjoint-set structure\n- [cycle_detection](algorithms/graph/cycle_detection.py) &mdash; detect cycles in a directed graph\n- [dijkstra](algorithms/graph/dijkstra.py) &mdash; single-source shortest path for non-negative weights\n- [dijkstra_heapq](algorithms/graph/dijkstra_heapq.py) &mdash; heap-optimised Dijkstra in O((V+E) log V) for sparse graphs\n- [find_all_cliques](algorithms/graph/find_all_cliques.py) &mdash; Bron-Kerbosch algorithm for finding all cliques\n- [find_path](algorithms/graph/find_path.py) &mdash; find paths between two vertices\n- [kahns_algorithm](algorithms/graph/kahns_algorithm.py) &mdash; topological sort via in-degree counting (Kahn's)\n- [markov_chain](algorithms/graph/markov_chain.py) &mdash; Markov chain probability modeling\n- [maximum_flow](algorithms/graph/maximum_flow.py) &mdash; compute maximum flow in a flow network\n- [maximum_flow (BFS)](algorithms/graph/maximum_flow_bfs.py) &mdash; Edmonds-Karp max-flow (BFS-based Ford-Fulkerson)\n- [maximum_flow (DFS)](algorithms/graph/maximum_flow_dfs.py) &mdash; Ford-Fulkerson max-flow via DFS augmenting paths\n- [maze_search (BFS)](algorithms/graph/maze_search_bfs.py) &mdash; find shortest path through a maze using BFS\n- [maze_search (DFS)](algorithms/graph/maze_search_dfs.py) &mdash; find a path through a maze using DFS\n- [minimum_spanning_tree](algorithms/graph/minimum_spanning_tree.py) &mdash; Kruskal's minimum spanning tree\n- [pacific_atlantic](algorithms/graph/pacific_atlantic.py) &mdash; find cells that can flow to both oceans\n- [path_between_two_vertices_in_digraph](algorithms/graph/path_between_two_vertices_in_digraph.py) &mdash; check if a path exists in a directed graph\n- [prims_minimum_spanning](algorithms/graph/prims_minimum_spanning.py) &mdash; Prim's minimum spanning tree\n- [satisfiability](algorithms/graph/satisfiability.py) &mdash; 2-SAT satisfiability via implication graph\n- [shortest_distance_from_all_buildings](algorithms/graph/shortest_distance_from_all_buildings.py) &mdash; find the optimal meeting point in a grid\n- [strongly_connected_components (Kosaraju)](algorithms/graph/strongly_connected_components_kosaraju.py) &mdash; Kosaraju's SCC algorithm\n- [sudoku_solver](algorithms/graph/sudoku_solver.py) &mdash; solve a Sudoku puzzle using constraint backtracking\n- [tarjan](algorithms/graph/tarjan.py) &mdash; Tarjan's strongly connected components algorithm\n- [topological_sort (BFS)](algorithms/graph/topological_sort_bfs.py) &mdash; topological ordering using BFS (Kahn's variant)\n- [topological_sort (DFS)](algorithms/graph/topological_sort_dfs.py) &mdash; topological ordering using DFS post-order\n- [transitive_closure (DFS)](algorithms/graph/transitive_closure_dfs.py) &mdash; compute the transitive closure of a graph\n- [traversal](algorithms/graph/traversal.py) &mdash; BFS and DFS graph traversal\n- [walls_and_gates](algorithms/graph/walls_and_gates.py) &mdash; fill each empty room with distance to nearest gate\n- [word_ladder](algorithms/graph/word_ladder.py) &mdash; shortest word-to-word transformation sequence\n\n### Greedy\n\n- [gale_shapley](algorithms/greedy/gale_shapley.py) &mdash; stable matching for bipartite preferences (Gale-Shapley)\n- [max_contiguous_subsequence_sum](algorithms/greedy/max_contiguous_subsequence_sum.py) &mdash; maximum contiguous subarray sum (Kadane's algorithm)\n\n### Heap\n\n- [k_closest_points](algorithms/heap/k_closest_points.py) &mdash; find k points closest to the origin\n- [merge_sorted_k_lists](algorithms/heap/merge_sorted_k_lists.py) &mdash; merge k sorted linked lists using a min-heap\n- [skyline](algorithms/heap/skyline.py) &mdash; compute the skyline silhouette from building rectangles\n- [sliding_window_max](algorithms/heap/sliding_window_max.py) &mdash; maximum value in each sliding window position\n\n### Linked List\n\n- [add_two_numbers](algorithms/linked_list/add_two_numbers.py) &mdash; add two numbers stored as reversed linked lists\n- [copy_random_pointer](algorithms/linked_list/copy_random_pointer.py) &mdash; deep-copy a linked list with random pointers\n- [delete_node](algorithms/linked_list/delete_node.py) &mdash; delete a node given only a reference to it\n- [first_cyclic_node](algorithms/linked_list/first_cyclic_node.py) &mdash; find the first node where a cycle begins (Floyd's)\n- [intersection](algorithms/linked_list/intersection.py) &mdash; find the intersection point of two singly linked lists\n- [is_cyclic](algorithms/linked_list/is_cyclic.py) &mdash; detect whether a linked list has a cycle\n- [is_palindrome](algorithms/linked_list/is_palindrome.py) &mdash; check if a linked list reads the same forwards and backwards\n- [is_sorted](algorithms/linked_list/is_sorted.py) &mdash; check if a linked list is sorted in order\n- [kth_to_last](algorithms/linked_list/kth_to_last.py) &mdash; find the k-th element from the end\n- [merge_two_list](algorithms/linked_list/merge_two_list.py) &mdash; merge two sorted linked lists into one\n- [partition](algorithms/linked_list/partition.py) &mdash; partition a list around a pivot value\n- [remove_duplicates](algorithms/linked_list/remove_duplicates.py) &mdash; remove duplicate values from a linked list\n- [remove_range](algorithms/linked_list/remove_range.py) &mdash; remove nodes within a given index range\n- [reverse](algorithms/linked_list/reverse.py) &mdash; reverse a linked list iteratively and recursively\n- [rotate_list](algorithms/linked_list/rotate_list.py) &mdash; rotate a list right by k positions\n- [swap_in_pairs](algorithms/linked_list/swap_in_pairs.py) &mdash; swap every two adjacent nodes\n\n### Map\n\n- [is_anagram](algorithms/map/is_anagram.py) &mdash; check if two strings are anagrams via character counting\n- [is_isomorphic](algorithms/map/is_isomorphic.py) &mdash; check if two strings have the same character mapping structure\n- [longest_common_subsequence](algorithms/map/longest_common_subsequence.py) &mdash; longest common substring using a hash map\n- [longest_palindromic_subsequence](algorithms/map/longest_palindromic_subsequence.py) &mdash; longest palindromic substring via hash map\n- [randomized_set](algorithms/map/randomized_set.py) &mdash; O(1) insert, delete, and get-random data structure\n- [valid_sudoku](algorithms/map/valid_sudoku.py) &mdash; validate a Sudoku board configuration\n- [word_pattern](algorithms/map/word_pattern.py) &mdash; check if a string follows a given pattern mapping\n\n### Math\n\n- [base_conversion](algorithms/math/base_conversion.py) &mdash; convert integers between arbitrary number bases\n- [chinese_remainder_theorem](algorithms/math/chinese_remainder_theorem.py) &mdash; solve a system of modular congruences\n- [combination](algorithms/math/combination.py) &mdash; compute binomial coefficients (n choose r)\n- [cosine_similarity](algorithms/math/cosine_similarity.py) &mdash; compute cosine similarity between two vectors\n- [decimal_to_binary_ip](algorithms/math/decimal_to_binary_ip.py) &mdash; convert an IP address between decimal and binary\n- [diffie_hellman_key_exchange](algorithms/math/diffie_hellman_key_exchange.py) &mdash; Diffie-Hellman cryptographic key exchange\n- [distance_between_two_points](algorithms/math/distance_between_two_points.py) &mdash; Euclidean distance in 2D space\n- [euler_totient](algorithms/math/euler_totient.py) &mdash; count integers up to n that are coprime to n\n- [extended_gcd](algorithms/math/extended_gcd.py) &mdash; extended Euclidean algorithm (GCD with coefficients)\n- [factorial](algorithms/math/factorial.py) &mdash; compute n! iteratively and recursively\n- [fft](algorithms/math/fft.py) &mdash; Fast Fourier Transform (Cooley-Tukey)\n- [find_order_simple](algorithms/math/find_order_simple.py) &mdash; find the multiplicative order of an element mod n\n- [find_primitive_root_simple](algorithms/math/find_primitive_root_simple.py) &mdash; find a primitive root modulo a prime\n- [gcd](algorithms/math/gcd.py) &mdash; greatest common divisor and least common multiple\n- [generate_strobogrammtic](algorithms/math/generate_strobogrammtic.py) &mdash; generate strobogrammatic numbers of length n\n- [goldbach](algorithms/math/goldbach.py) &mdash; decompose an even number into a sum of two primes (Goldbach's conjecture)\n- [hailstone](algorithms/math/hailstone.py) &mdash; Collatz conjecture (hailstone) sequence\n- [is_strobogrammatic](algorithms/math/is_strobogrammatic.py) &mdash; check if a number looks the same upside-down\n- [krishnamurthy_number](algorithms/math/krishnamurthy_number.py) &mdash; check if a number equals the sum of the factorials of its digits\n- [linear_regression](algorithms/math/linear_regression.py) &mdash; ordinary least-squares linear regression with R² and RMSE\n- [magic_number](algorithms/math/magic_number.py) &mdash; check if a number is a magic number\n- [manhattan_distance](algorithms/math/manhattan_distance.py) &mdash; compute Manhattan (L1) distance between two points in any dimension\n- [modular_exponential](algorithms/math/modular_exponential.py) &mdash; compute (base^exp) mod m efficiently\n- [modular_inverse](algorithms/math/modular_inverse.py) &mdash; compute the modular multiplicative inverse\n- [next_bigger](algorithms/math/next_bigger.py) &mdash; next larger number with the same digits\n- [next_perfect_square](algorithms/math/next_perfect_square.py) &mdash; find the next perfect square after n\n- [nth_digit](algorithms/math/nth_digit.py) &mdash; find the n-th digit in the sequence 1, 2, 3, ...\n- [num_digits](algorithms/math/num_digits.py) &mdash; count the number of digits in an integer\n- [num_perfect_squares](algorithms/math/num_perfect_squares.py) &mdash; minimum perfect squares that sum to n\n- [polynomial](algorithms/math/polynomial.py) &mdash; polynomial and monomial arithmetic operations\n- [polynomial_division](algorithms/math/polynomial_division.py) &mdash; polynomial long division returning quotient and remainder\n- [power](algorithms/math/power.py) &mdash; compute x^n via binary exponentiation\n- [prime_check](algorithms/math/prime_check.py) &mdash; check if a number is prime\n- [primes_sieve_of_eratosthenes](algorithms/math/primes_sieve_of_eratosthenes.py) &mdash; generate primes up to n using the Sieve of Eratosthenes\n- [pythagoras](algorithms/math/pythagoras.py) &mdash; Pythagorean theorem calculations\n- [rabin_miller](algorithms/math/rabin_miller.py) &mdash; Miller-Rabin probabilistic primality test\n- [recursive_binomial_coefficient](algorithms/math/recursive_binomial_coefficient.py) &mdash; binomial coefficient via Pascal's triangle recursion\n- [rsa](algorithms/math/rsa.py) &mdash; RSA public-key encryption and decryption\n- [sqrt_precision_factor](algorithms/math/sqrt_precision_factor.py) &mdash; square root to arbitrary precision (Newton's method)\n- [summing_digits](algorithms/math/summing_digits.py) &mdash; recursively sum the digits of a number\n- [surface_area_of_torus](algorithms/math/surface_area_of_torus.py) &mdash; calculate the surface area of a torus\n- [symmetry_group_cycle_index](algorithms/math/symmetry_group_cycle_index.py) &mdash; cycle index polynomials for symmetry groups\n\n### Matrix\n\n- [bomb_enemy](algorithms/matrix/bomb_enemy.py) &mdash; maximize enemies killed by a single bomb placement\n- [cholesky_matrix_decomposition](algorithms/matrix/cholesky_matrix_decomposition.py) &mdash; Cholesky decomposition of a positive-definite matrix\n- [copy_transform](algorithms/matrix/copy_transform.py) &mdash; copy and transform a matrix\n- [count_paths](algorithms/matrix/count_paths.py) &mdash; count paths from top-left to bottom-right of a grid\n- [crout_matrix_decomposition](algorithms/matrix/crout_matrix_decomposition.py) &mdash; Crout's LU matrix decomposition\n- [matrix_exponentiation](algorithms/matrix/matrix_exponentiation.py) &mdash; raise a matrix to the n-th power efficiently\n- [matrix_inversion](algorithms/matrix/matrix_inversion.py) &mdash; compute the inverse of a square matrix\n- [multiply](algorithms/matrix/multiply.py) &mdash; standard and Strassen matrix multiplication\n- [rotate_image](algorithms/matrix/rotate_image.py) &mdash; rotate an n x n matrix 90 degrees in-place\n- [search_in_sorted_matrix](algorithms/matrix/search_in_sorted_matrix.py) &mdash; search in a row- and column-sorted matrix\n- [sort_matrix_diagonally](algorithms/matrix/sort_matrix_diagonally.py) &mdash; sort each diagonal of a matrix independently\n- [sparse_dot_vector](algorithms/matrix/sparse_dot_vector.py) &mdash; dot product of two sparse vectors\n- [sparse_mul](algorithms/matrix/sparse_mul.py) &mdash; multiply two sparse matrices efficiently\n- [spiral_traversal](algorithms/matrix/spiral_traversal.py) &mdash; traverse a matrix in spiral order\n- [sudoku_validator](algorithms/matrix/sudoku_validator.py) &mdash; validate that a Sudoku board follows all rules\n- [sum_sub_squares](algorithms/matrix/sum_sub_squares.py) &mdash; sum of all k x k sub-squares in a matrix\n\n### Queue\n\n- [max_sliding_window](algorithms/queue/max_sliding_window.py) &mdash; maximum in each sliding window using a deque\n- [moving_average](algorithms/queue/moving_average.py) &mdash; compute a running moving average from a stream\n- [reconstruct_queue](algorithms/queue/reconstruct_queue.py) &mdash; reconstruct a queue from (height, count) pairs\n- [zigzagiterator](algorithms/queue/zigzagiterator.py) &mdash; alternate elements from multiple iterators\n\n### Searching\n\n- [binary_search](algorithms/searching/binary_search.py) &mdash; search a sorted array in O(log n)\n- [exponential_search](algorithms/searching/exponential_search.py) &mdash; search a sorted array by doubling the range then binary searching\n- [find_min_rotate](algorithms/searching/find_min_rotate.py) &mdash; find the minimum in a rotated sorted array\n- [first_occurrence](algorithms/searching/first_occurrence.py) &mdash; find the first occurrence of a target value\n- [generalized_binary_search](algorithms/searching/generalized_binary_search.py) &mdash; binary search with a custom predicate\n- [interpolation_search](algorithms/searching/interpolation_search.py) &mdash; search using value-based interpolation\n- [jump_search](algorithms/searching/jump_search.py) &mdash; search a sorted array by jumping in fixed blocks\n- [last_occurrence](algorithms/searching/last_occurrence.py) &mdash; find the last occurrence of a target value\n- [linear_search](algorithms/searching/linear_search.py) &mdash; sequential scan through an unsorted array\n- [next_greatest_letter](algorithms/searching/next_greatest_letter.py) &mdash; find the smallest letter greater than a target\n- [search_insert](algorithms/searching/search_insert.py) &mdash; find the insertion position for a target value\n- [search_range](algorithms/searching/search_range.py) &mdash; find the first and last positions of a target\n- [search_rotate](algorithms/searching/search_rotate.py) &mdash; search in a rotated sorted array\n- [sentinel_search](algorithms/searching/sentinel_search.py) &mdash; linear search optimized by placing a sentinel at the end\n- [ternary_search](algorithms/searching/ternary_search.py) &mdash; search by dividing the array into three parts\n- [two_sum](algorithms/searching/two_sum.py) &mdash; find two numbers that sum to a target\n\n### Set\n\n- [find_keyboard_row](algorithms/set/find_keyboard_row.py) &mdash; filter words that can be typed on one keyboard row\n- [randomized_set](algorithms/set/randomized_set.py) &mdash; O(1) insert, delete, and random-element access\n- [set_covering](algorithms/set/set_covering.py) &mdash; greedy approximation for the set cover problem\n\n### Sorting\n\n- [bead_sort](algorithms/sorting/bead_sort.py) &mdash; gravity-based natural sorting (bead/abacus sort)\n- [bitonic_sort](algorithms/sorting/bitonic_sort.py) &mdash; parallel-friendly comparison sort via bitonic sequences\n- [bogo_sort](algorithms/sorting/bogo_sort.py) &mdash; random permutation sort (intentionally inefficient)\n- [bubble_sort](algorithms/sorting/bubble_sort.py) &mdash; repeatedly swap adjacent out-of-order elements\n- [bucket_sort](algorithms/sorting/bucket_sort.py) &mdash; distribute elements into buckets, then sort each\n- [cocktail_shaker_sort](algorithms/sorting/cocktail_shaker_sort.py) &mdash; bidirectional bubble sort\n- [comb_sort](algorithms/sorting/comb_sort.py) &mdash; bubble sort improved with a shrinking gap\n- [counting_sort](algorithms/sorting/counting_sort.py) &mdash; sort integers by counting occurrences\n- [cycle_sort](algorithms/sorting/cycle_sort.py) &mdash; in-place sort that minimizes total writes\n- [exchange_sort](algorithms/sorting/exchange_sort.py) &mdash; simple pairwise comparison and exchange\n- [gnome_sort](algorithms/sorting/gnome_sort.py) &mdash; sort by swapping elements backward until ordered\n- [heap_sort](algorithms/sorting/heap_sort.py) &mdash; sort via a binary heap (in-place, O(n log n))\n- [insertion_sort](algorithms/sorting/insertion_sort.py) &mdash; build a sorted portion one element at a time\n- [meeting_rooms](algorithms/sorting/meeting_rooms.py) &mdash; determine if meeting intervals overlap\n- [merge_sort](algorithms/sorting/merge_sort.py) &mdash; divide-and-conquer stable sort (O(n log n))\n- [pancake_sort](algorithms/sorting/pancake_sort.py) &mdash; sort using only prefix reversals\n- [pigeonhole_sort](algorithms/sorting/pigeonhole_sort.py) &mdash; sort by placing elements into pigeonhole buckets\n- [quick_sort](algorithms/sorting/quick_sort.py) &mdash; partition-based divide-and-conquer sort\n- [radix_sort](algorithms/sorting/radix_sort.py) &mdash; non-comparative sort processing one digit at a time\n- [selection_sort](algorithms/sorting/selection_sort.py) &mdash; repeatedly select the minimum and swap it forward\n- [shell_sort](algorithms/sorting/shell_sort.py) &mdash; generalized insertion sort with a decreasing gap sequence\n- [sort_colors](algorithms/sorting/sort_colors.py) &mdash; Dutch national flag three-way partition\n- [stooge_sort](algorithms/sorting/stooge_sort.py) &mdash; recursive sort by dividing into overlapping thirds\n- [wiggle_sort](algorithms/sorting/wiggle_sort.py) &mdash; rearrange into an alternating peak-valley pattern\n\n### Stack\n\n- [is_consecutive](algorithms/stack/is_consecutive.py) &mdash; check if stack elements are consecutive integers\n- [is_sorted](algorithms/stack/is_sorted.py) &mdash; check if a stack is sorted in ascending order\n- [longest_abs_path](algorithms/stack/longest_abs_path.py) &mdash; find the longest absolute file path in a file system string\n- [ordered_stack](algorithms/stack/ordered_stack.py) &mdash; maintain a stack in sorted order\n- [remove_min](algorithms/stack/remove_min.py) &mdash; remove the minimum element from a stack\n- [simplify_path](algorithms/stack/simplify_path.py) &mdash; simplify a Unix-style file path\n- [stutter](algorithms/stack/stutter.py) &mdash; duplicate each element in a stack\n- [switch_pairs](algorithms/stack/switch_pairs.py) &mdash; swap adjacent pairs of stack elements\n- [valid_parenthesis](algorithms/stack/valid_parenthesis.py) &mdash; check for balanced parentheses / brackets\n\n### Streaming\n\n- [misra_gries](algorithms/streaming/misra_gries.py) &mdash; approximate frequent-item detection in a data stream\n- [one_sparse_recovery](algorithms/streaming/one_sparse_recovery.py) &mdash; recover a single non-zero element from a stream\n\n### String\n\n- [add_binary](algorithms/string/add_binary.py) &mdash; add two binary number strings\n- [alphabet_board_path](algorithms/string/alphabet_board_path.py) &mdash; navigate a 5×5 alphabet board to spell a target word\n- [atbash_cipher](algorithms/string/atbash_cipher.py) &mdash; Atbash substitution cipher (reverse the alphabet)\n- [breaking_bad](algorithms/string/breaking_bad.py) &mdash; spell a string using periodic-table element symbols\n- [caesar_cipher](algorithms/string/caesar_cipher.py) &mdash; Caesar shift cipher encryption / decryption\n- [check_pangram](algorithms/string/check_pangram.py) &mdash; check if a string contains every letter of the alphabet\n- [contain_string](algorithms/string/contain_string.py) &mdash; find a substring in a string (strStr)\n- [count_binary_substring](algorithms/string/count_binary_substring.py) &mdash; count substrings with equal consecutive 0s and 1s\n- [decode_string](algorithms/string/decode_string.py) &mdash; decode a run-length encoded string like `3[a2[c]]`\n- [delete_reoccurring](algorithms/string/delete_reoccurring.py) &mdash; remove consecutive duplicate characters\n- [domain_extractor](algorithms/string/domain_extractor.py) &mdash; extract a domain name from a URL\n- [encode_decode](algorithms/string/encode_decode.py) &mdash; encode and decode a list of strings\n- [first_unique_char](algorithms/string/first_unique_char.py) &mdash; find the first non-repeating character\n- [fizzbuzz](algorithms/string/fizzbuzz.py) &mdash; classic FizzBuzz problem\n- [group_anagrams](algorithms/string/group_anagrams.py) &mdash; group strings that are anagrams of each other\n- [int_to_roman](algorithms/string/int_to_roman.py) &mdash; convert an integer to a Roman numeral string\n- [is_palindrome](algorithms/string/is_palindrome.py) &mdash; check if a string reads the same forwards and backwards\n- [is_rotated](algorithms/string/is_rotated.py) &mdash; check if one string is a rotation of another\n- [judge_circle](algorithms/string/judge_circle.py) &mdash; determine if a sequence of moves returns to the origin\n- [knuth_morris_pratt](algorithms/string/knuth_morris_pratt.py) &mdash; KMP linear-time pattern matching\n- [license_number](algorithms/string/license_number.py) &mdash; reformat a license key string with dashes\n- [longest_common_prefix](algorithms/string/longest_common_prefix.py) &mdash; find the longest common prefix among strings\n- [longest_palindromic_substring](algorithms/string/longest_palindromic_substring.py) &mdash; find the longest palindromic substring\n- [make_sentence](algorithms/string/make_sentence.py) &mdash; break a string into valid dictionary words\n- [manacher](algorithms/string/manacher.py) &mdash; find the longest palindromic substring in O(n) time\n- [merge_string_checker](algorithms/string/merge_string_checker.py) &mdash; check if a string is a valid merge of two others\n- [min_distance](algorithms/string/min_distance.py) &mdash; minimum deletions to make two strings equal\n- [multiply_strings](algorithms/string/multiply_strings.py) &mdash; multiply two numbers represented as strings\n- [one_edit_distance](algorithms/string/one_edit_distance.py) &mdash; check if two strings are exactly one edit apart\n- [panagram](algorithms/string/panagram.py) &mdash; find missing letters to complete a pangram\n- [rabin_karp](algorithms/string/rabin_karp.py) &mdash; Rabin-Karp rolling-hash pattern matching\n- [repeat_string](algorithms/string/repeat_string.py) &mdash; minimum string repeats to contain a substring\n- [repeat_substring](algorithms/string/repeat_substring.py) &mdash; check if a string is built from a repeating pattern\n- [reverse_string](algorithms/string/reverse_string.py) &mdash; reverse a string in-place\n- [reverse_vowel](algorithms/string/reverse_vowel.py) &mdash; reverse only the vowels in a string\n- [reverse_words](algorithms/string/reverse_words.py) &mdash; reverse the order of words in a string\n- [roman_to_int](algorithms/string/roman_to_int.py) &mdash; convert a Roman numeral string to an integer\n- [rotate](algorithms/string/rotate.py) &mdash; rotate a string by k positions\n- [strip_url_params](algorithms/string/strip_url_params.py) &mdash; remove duplicate query parameters from a URL\n- [swap_characters](algorithms/string/swap_characters.py) &mdash; check if one character swap can make two strings equal\n- [strong_password](algorithms/string/strong_password.py) &mdash; check minimum changes needed for a strong password\n- [text_justification](algorithms/string/text_justification.py) &mdash; justify text lines to a specified width\n- [unique_morse](algorithms/string/unique_morse.py) &mdash; count unique Morse code representations of words\n- [validate_coordinates](algorithms/string/validate_coordinates.py) &mdash; validate geographic latitude/longitude coordinates\n- [word_squares](algorithms/string/word_squares.py) &mdash; find all valid word squares from a word list\n- [z_algorithm](algorithms/string/z_algorithm.py) &mdash; Z-array computation for linear-time pattern matching\n\n### Tree\n\n- [bin_tree_to_list](algorithms/tree/bin_tree_to_list.py) &mdash; convert a binary tree to a doubly linked list\n- [binary_tree_paths](algorithms/tree/binary_tree_paths.py) &mdash; enumerate all root-to-leaf paths\n- [binary_tree_views](algorithms/tree/binary_tree_views.py) &mdash; left, right, top, and bottom views of a binary tree\n- [bst_array_to_bst](algorithms/tree/bst_array_to_bst.py) &mdash; convert a sorted array into a height-balanced BST\n- [bst_closest_value](algorithms/tree/bst_closest_value.py) &mdash; find the value closest to a target in a BST\n- [bst_count_left_node](algorithms/tree/bst_count_left_node.py) &mdash; count the number of left-child nodes\n- [bst_delete_node](algorithms/tree/bst_delete_node.py) &mdash; delete a node from a BST while preserving order\n- [bst_depth_sum](algorithms/tree/bst_depth_sum.py) &mdash; sum of node values weighted by their depth\n- [bst_height](algorithms/tree/bst_height.py) &mdash; calculate the height of a binary tree\n- [bst_is_bst](algorithms/tree/bst_is_bst.py) &mdash; validate the binary search tree property\n- [bst_iterator](algorithms/tree/bst_iterator.py) &mdash; lazy in-order iterator for a BST\n- [bst_kth_smallest](algorithms/tree/bst_kth_smallest.py) &mdash; find the k-th smallest element in a BST\n- [bst_lowest_common_ancestor](algorithms/tree/bst_lowest_common_ancestor.py) &mdash; lowest common ancestor exploiting BST ordering\n- [bst_num_empty](algorithms/tree/bst_num_empty.py) &mdash; count empty (null) branches in a tree\n- [bst_predecessor](algorithms/tree/bst_predecessor.py) &mdash; find the in-order predecessor of a BST node\n- [bst_serialize_deserialize](algorithms/tree/bst_serialize_deserialize.py) &mdash; serialize a BST to a string and back\n- [bst_successor](algorithms/tree/bst_successor.py) &mdash; find the in-order successor of a BST node\n- [bst_unique_bst](algorithms/tree/bst_unique_bst.py) &mdash; count structurally unique BSTs for n keys (Catalan number)\n- [bst_validate_bst](algorithms/tree/bst_validate_bst.py) &mdash; validate a BST using min/max range constraints\n- [construct_tree_postorder_preorder](algorithms/tree/construct_tree_postorder_preorder.py) &mdash; reconstruct a tree from pre-order and post-order traversals\n- [deepest_left](algorithms/tree/deepest_left.py) &mdash; find the deepest left leaf node\n- [invert_tree](algorithms/tree/invert_tree.py) &mdash; mirror a binary tree (swap all left/right children)\n- [is_balanced](algorithms/tree/is_balanced.py) &mdash; check if a tree is height-balanced\n- [is_subtree](algorithms/tree/is_subtree.py) &mdash; check if one tree is a subtree of another\n- [is_symmetric](algorithms/tree/is_symmetric.py) &mdash; check if a tree is a mirror of itself\n- [longest_consecutive](algorithms/tree/longest_consecutive.py) &mdash; longest consecutive-value sequence in a tree\n- [lowest_common_ancestor](algorithms/tree/lowest_common_ancestor.py) &mdash; find the lowest common ancestor of two nodes\n- [max_height](algorithms/tree/max_height.py) &mdash; maximum depth (height) of a binary tree\n- [max_path_sum](algorithms/tree/max_path_sum.py) &mdash; maximum sum along any path between two nodes\n- [min_height](algorithms/tree/min_height.py) &mdash; minimum depth from root to the nearest leaf\n- [path_sum](algorithms/tree/path_sum.py) &mdash; check if any root-to-leaf path sums to a target\n- [path_sum2](algorithms/tree/path_sum2.py) &mdash; find all root-to-leaf paths that sum to a target\n- [pretty_print](algorithms/tree/pretty_print.py) &mdash; pretty-print a binary tree to the console\n- [same_tree](algorithms/tree/same_tree.py) &mdash; check if two binary trees are structurally identical\n- [traversal_inorder](algorithms/tree/traversal_inorder.py) &mdash; in-order traversal (left, root, right)\n- [traversal_level_order](algorithms/tree/traversal_level_order.py) &mdash; level-order (breadth-first) traversal\n- [traversal_postorder](algorithms/tree/traversal_postorder.py) &mdash; post-order traversal (left, right, root)\n- [traversal_preorder](algorithms/tree/traversal_preorder.py) &mdash; pre-order traversal (root, left, right)\n- [traversal_zigzag](algorithms/tree/traversal_zigzag.py) &mdash; zigzag (alternating direction) level-order traversal\n- [trie_add_and_search](algorithms/tree/trie_add_and_search.py) &mdash; trie with wildcard `.` search support\n\n## Contributing\n\nThanks for your interest in contributing! There are many ways to get involved. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n## Maintainers\n\n- [Keon Kim](https://github.com/keon)\n\n## Contributors\n\nThanks to [all the contributors](https://github.com/keon/algorithms/graphs/contributors) who helped build this repo.\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "algorithms/__init__.py",
    "content": "\"\"\"Pythonic data structures and algorithms for education.\n\nShared types are available at the top level::\n\n    >>> from algorithms import TreeNode, ListNode, Graph\n    >>> from algorithms.data_structures import BinaryHeap, HashTable\n    >>> from algorithms.graph import dijkstra\n\"\"\"\n\nimport algorithms.data_structures as data_structures  # noqa: F401\nfrom algorithms.common import Graph, ListNode, TreeNode\n\n__all__ = [\"TreeNode\", \"ListNode\", \"Graph\", \"data_structures\"]\n"
  },
  {
    "path": "algorithms/array/__init__.py",
    "content": "from .delete_nth import delete_nth, delete_nth_naive\nfrom .flatten import flatten, flatten_iter\nfrom .garage import garage\nfrom .josephus import josephus\nfrom .limit import limit\nfrom .longest_non_repeat import (\n    get_longest_non_repeat_v1,\n    get_longest_non_repeat_v2,\n    get_longest_non_repeat_v3,\n    longest_non_repeat_v1,\n    longest_non_repeat_v2,\n)\nfrom .max_ones_index import max_ones_index\nfrom .merge_intervals import Interval, merge_intervals\nfrom .missing_ranges import missing_ranges\nfrom .move_zeros import move_zeros\nfrom .n_sum import n_sum\nfrom .plus_one import plus_one_v1, plus_one_v2, plus_one_v3\nfrom .remove_duplicates import remove_duplicates\nfrom .rotate import rotate_v1, rotate_v2, rotate_v3\nfrom .summarize_ranges import summarize_ranges\nfrom .three_sum import three_sum\nfrom .top_1 import top_1\nfrom .trimmean import trimmean\nfrom .two_sum import two_sum\n\n__all__ = [\n    \"delete_nth\",\n    \"delete_nth_naive\",\n    \"flatten\",\n    \"flatten_iter\",\n    \"garage\",\n    \"josephus\",\n    \"limit\",\n    \"get_longest_non_repeat_v1\",\n    \"get_longest_non_repeat_v2\",\n    \"get_longest_non_repeat_v3\",\n    \"longest_non_repeat_v1\",\n    \"longest_non_repeat_v2\",\n    \"max_ones_index\",\n    \"Interval\",\n    \"merge_intervals\",\n    \"missing_ranges\",\n    \"move_zeros\",\n    \"n_sum\",\n    \"plus_one_v1\",\n    \"plus_one_v2\",\n    \"plus_one_v3\",\n    \"remove_duplicates\",\n    \"rotate_v1\",\n    \"rotate_v2\",\n    \"rotate_v3\",\n    \"summarize_ranges\",\n    \"three_sum\",\n    \"top_1\",\n    \"trimmean\",\n    \"two_sum\",\n]\n"
  },
  {
    "path": "algorithms/array/delete_nth.py",
    "content": "\"\"\"\nDelete Nth Occurrence\n\nGiven a list and a number N, create a new list that contains each element\nof the original list at most N times, without reordering.\n\nReference: https://www.geeksforgeeks.org/remove-duplicates-from-an-array/\n\nComplexity:\n    delete_nth_naive:\n        Time:  O(n^2) due to list.count()\n        Space: O(n)\n    delete_nth:\n        Time:  O(n)\n        Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef delete_nth_naive(array: list[int], n: int) -> list[int]:\n    \"\"\"Keep at most n copies of each element using naive counting.\n\n    Args:\n        array: Source list of integers.\n        n: Maximum number of allowed occurrences per element.\n\n    Returns:\n        New list with each element appearing at most n times.\n\n    Examples:\n        >>> delete_nth_naive([1, 2, 3, 1, 2, 1, 2, 3], 2)\n        [1, 2, 3, 1, 2, 3]\n    \"\"\"\n    result = []\n    for num in array:\n        if result.count(num) < n:\n            result.append(num)\n    return result\n\n\ndef delete_nth(array: list[int], n: int) -> list[int]:\n    \"\"\"Keep at most n copies of each element using a hash table.\n\n    Args:\n        array: Source list of integers.\n        n: Maximum number of allowed occurrences per element.\n\n    Returns:\n        New list with each element appearing at most n times.\n\n    Examples:\n        >>> delete_nth([1, 2, 3, 1, 2, 1, 2, 3], 2)\n        [1, 2, 3, 1, 2, 3]\n    \"\"\"\n    result = []\n    counts = collections.defaultdict(int)\n\n    for element in array:\n        if counts[element] < n:\n            result.append(element)\n            counts[element] += 1\n\n    return result\n"
  },
  {
    "path": "algorithms/array/flatten.py",
    "content": "\"\"\"\nFlatten Arrays\n\nGiven an array that may contain nested arrays, produce a single\nflat resultant array.\n\nReference: https://en.wikipedia.org/wiki/Flatten_(higher-order_function)\n\nComplexity:\n    Time:  O(n) where n is the total number of elements\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Generator, Iterable\nfrom typing import Any\n\n\ndef flatten(input_arr: Iterable[Any], output_arr: list[Any] | None = None) -> list[Any]:\n    \"\"\"Recursively flatten a nested iterable into a single list.\n\n    Args:\n        input_arr: A potentially nested iterable to flatten.\n        output_arr: Accumulator list for recursive calls (internal use).\n\n    Returns:\n        A flat list containing all leaf elements.\n\n    Examples:\n        >>> flatten([2, 1, [3, [4, 5], 6], 7, [8]])\n        [2, 1, 3, 4, 5, 6, 7, 8]\n    \"\"\"\n    if output_arr is None:\n        output_arr = []\n    for element in input_arr:\n        if not isinstance(element, str) and isinstance(element, Iterable):\n            flatten(element, output_arr)\n        else:\n            output_arr.append(element)\n    return output_arr\n\n\ndef flatten_iter(iterable: Iterable[Any]) -> Generator[Any, None, None]:\n    \"\"\"Lazily flatten a nested iterable, yielding one element at a time.\n\n    Args:\n        iterable: A potentially nested iterable to flatten.\n\n    Returns:\n        A generator producing one-dimensional output.\n\n    Examples:\n        >>> list(flatten_iter([2, 1, [3, [4, 5], 6], 7, [8]]))\n        [2, 1, 3, 4, 5, 6, 7, 8]\n    \"\"\"\n    for element in iterable:\n        if not isinstance(element, str) and isinstance(element, Iterable):\n            yield from flatten_iter(element)\n        else:\n            yield element\n"
  },
  {
    "path": "algorithms/array/garage.py",
    "content": "\"\"\"\nGarage Parking Rearrangement\n\nThere is a parking lot with only one empty spot (represented by 0). Given the\ninitial and final states, find the minimum number of moves to rearrange the lot.\nEach move swaps a car into the empty spot.\n\nReference: https://en.wikipedia.org/wiki/15_puzzle\n\nComplexity:\n    Time:  O(n^2) worst case\n    Space: O(n) for storing the sequence\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef garage(initial: list[int], final: list[int]) -> tuple[int, list[list[int]]]:\n    \"\"\"Find the minimum swaps to rearrange a parking lot from initial to final state.\n\n    Args:\n        initial: Starting arrangement where 0 represents the empty spot.\n        final: Desired arrangement where 0 represents the empty spot.\n\n    Returns:\n        A tuple of (number_of_steps, sequence_of_states) showing each\n        intermediate arrangement.\n\n    Examples:\n        >>> garage([1, 2, 3, 0, 4], [0, 3, 2, 1, 4])\n        (4, [[0, 2, 3, 1, 4], [2, 0, 3, 1, 4], [2, 3, 0, 1, 4], [0, 3, 2, 1, 4]])\n    \"\"\"\n    current = initial[::]\n    sequence = []\n    steps = 0\n\n    while current != final:\n        zero_pos = current.index(0)\n        if zero_pos != final.index(0):\n            target_car = final[zero_pos]\n            target_pos = current.index(target_car)\n            current[zero_pos], current[target_pos] = (\n                current[target_pos],\n                current[zero_pos],\n            )\n        else:\n            for i in range(len(current)):\n                if current[i] != final[i]:\n                    current[zero_pos], current[i] = current[i], current[zero_pos]\n                    break\n        sequence.append(current[::])\n        steps += 1\n\n    return steps, sequence\n"
  },
  {
    "path": "algorithms/array/josephus.py",
    "content": "\"\"\"\nJosephus Problem\n\nPeople sit in a circular fashion; every k-th person is eliminated until\neveryone has been removed. Yield the elimination order.\n\nReference: https://en.wikipedia.org/wiki/Josephus_problem\n\nComplexity:\n    Time:  O(n^2) due to list.pop at arbitrary index\n    Space: O(1) auxiliary (yields in-place)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Generator\nfrom typing import Any\n\n\ndef josephus(items: list[Any], skip: int) -> Generator[Any, None, None]:\n    \"\"\"Yield elements eliminated in Josephus-problem order.\n\n    Args:\n        items: List of participants arranged in a circle.\n        skip: Every *skip*-th person is eliminated each round.\n\n    Returns:\n        A generator yielding eliminated elements in order.\n\n    Examples:\n        >>> list(josephus([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))\n        [3, 6, 9, 4, 8, 5, 2, 7, 1]\n    \"\"\"\n    skip = skip - 1\n    index = 0\n    remaining = len(items)\n    while remaining > 0:\n        index = (skip + index) % remaining\n        yield items.pop(index)\n        remaining -= 1\n"
  },
  {
    "path": "algorithms/array/limit.py",
    "content": "\"\"\"\nLimit Array Values\n\nFilter an array to include only elements within a specified minimum and\nmaximum range (inclusive).\n\nReference: https://en.wikipedia.org/wiki/Clipping_(signal_processing)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef limit(\n    array: list[int],\n    min_lim: int | None = None,\n    max_lim: int | None = None,\n) -> list[int]:\n    \"\"\"Return elements of array that fall within [min_lim, max_lim].\n\n    Args:\n        array: Source list of integers.\n        min_lim: Minimum value (inclusive). Defaults to the array minimum.\n        max_lim: Maximum value (inclusive). Defaults to the array maximum.\n\n    Returns:\n        A new list containing only values within the specified range.\n\n    Examples:\n        >>> limit([1, 2, 3, 4, 5], 2, 4)\n        [2, 3, 4]\n    \"\"\"\n    if len(array) == 0:\n        return array\n\n    if min_lim is None:\n        min_lim = min(array)\n    if max_lim is None:\n        max_lim = max(array)\n\n    return list(filter(lambda x: min_lim <= x <= max_lim, array))\n"
  },
  {
    "path": "algorithms/array/longest_non_repeat.py",
    "content": "\"\"\"\nLongest Substring Without Repeating Characters\n\nGiven a string, find the length of the longest substring without repeating\ncharacters. Multiple algorithm variants are provided.\n\nReference: https://leetcode.com/problems/longest-substring-without-repeating-characters/\n\nComplexity:\n    Time:  O(n) for all variants\n    Space: O(min(n, m)) where m is the charset size\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef longest_non_repeat_v1(string: str) -> int:\n    \"\"\"Find the length of the longest substring without repeating characters.\n\n    Args:\n        string: Input string to search.\n\n    Returns:\n        Length of the longest non-repeating substring.\n\n    Examples:\n        >>> longest_non_repeat_v1(\"abcabcbb\")\n        3\n    \"\"\"\n    if string is None:\n        return 0\n    char_index = {}\n    max_length = 0\n    start = 0\n    for index in range(len(string)):\n        if string[index] in char_index:\n            start = max(char_index[string[index]], start)\n        char_index[string[index]] = index + 1\n        max_length = max(max_length, index - start + 1)\n    return max_length\n\n\ndef longest_non_repeat_v2(string: str) -> int:\n    \"\"\"Find the length of the longest substring without repeating characters.\n\n    Args:\n        string: Input string to search.\n\n    Returns:\n        Length of the longest non-repeating substring.\n\n    Examples:\n        >>> longest_non_repeat_v2(\"abcabcbb\")\n        3\n    \"\"\"\n    if string is None:\n        return 0\n    start, max_length = 0, 0\n    used_char = {}\n    for index, char in enumerate(string):\n        if char in used_char and start <= used_char[char]:\n            start = used_char[char] + 1\n        else:\n            max_length = max(max_length, index - start + 1)\n        used_char[char] = index\n    return max_length\n\n\ndef get_longest_non_repeat_v1(string: str) -> tuple[int, str]:\n    \"\"\"Find the longest substring without repeating characters.\n\n    Args:\n        string: Input string to search.\n\n    Returns:\n        A tuple of (length, substring) for the longest non-repeating substring.\n\n    Examples:\n        >>> get_longest_non_repeat_v1(\"abcabcbb\")\n        (3, 'abc')\n    \"\"\"\n    if string is None:\n        return 0, \"\"\n    substring = \"\"\n    char_index = {}\n    max_length = 0\n    start = 0\n    for index in range(len(string)):\n        if string[index] in char_index:\n            start = max(char_index[string[index]], start)\n        char_index[string[index]] = index + 1\n        if index - start + 1 > max_length:\n            max_length = index - start + 1\n            substring = string[start : index + 1]\n    return max_length, substring\n\n\ndef get_longest_non_repeat_v2(string: str) -> tuple[int, str]:\n    \"\"\"Find the longest substring without repeating characters.\n\n    Args:\n        string: Input string to search.\n\n    Returns:\n        A tuple of (length, substring) for the longest non-repeating substring.\n\n    Examples:\n        >>> get_longest_non_repeat_v2(\"abcabcbb\")\n        (3, 'abc')\n    \"\"\"\n    if string is None:\n        return 0, \"\"\n    substring = \"\"\n    start, max_length = 0, 0\n    used_char = {}\n    for index, char in enumerate(string):\n        if char in used_char and start <= used_char[char]:\n            start = used_char[char] + 1\n        else:\n            if index - start + 1 > max_length:\n                max_length = index - start + 1\n                substring = string[start : index + 1]\n        used_char[char] = index\n    return max_length, substring\n\n\ndef get_longest_non_repeat_v3(string: str) -> tuple[int, str]:\n    \"\"\"Find the longest substring without repeating characters using sliding window.\n\n    Args:\n        string: Input string to search.\n\n    Returns:\n        A tuple of (length, substring) for the longest non-repeating substring.\n\n    Examples:\n        >>> get_longest_non_repeat_v3(\"abcabcbb\")\n        (3, 'abc')\n    \"\"\"\n    longest_substring = \"\"\n    seen = set()\n    start_index = 0\n    for i in range(len(string)):\n        while string[i] in seen:\n            seen.remove(string[start_index])\n            start_index += 1\n        seen.add(string[i])\n        longest_substring = max(longest_substring, string[start_index : i + 1], key=len)\n    return len(longest_substring), longest_substring\n"
  },
  {
    "path": "algorithms/array/max_ones_index.py",
    "content": "\"\"\"\nMax Ones Index\n\nFind the index of the 0 that, when replaced with 1, produces the longest\ncontinuous sequence of 1s in a binary array. Returns -1 if no 0 exists.\n\nReference: https://www.geeksforgeeks.org/find-index-0-replaced-1-get-longest-continuous-sequence-1s-binary-array/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_ones_index(array: list[int]) -> int:\n    \"\"\"Find the index of 0 to replace with 1 for the longest run of 1s.\n\n    Args:\n        array: Binary array containing only 0s and 1s.\n\n    Returns:\n        Index of the 0 to flip, or -1 if no 0 exists.\n\n    Examples:\n        >>> max_ones_index([1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1])\n        3\n    \"\"\"\n    length = len(array)\n    max_count = 0\n    max_index = 0\n    prev_zero = -1\n    prev_prev_zero = -1\n\n    for current in range(length):\n        if array[current] == 0:\n            if current - prev_prev_zero > max_count:\n                max_count = current - prev_prev_zero\n                max_index = prev_zero\n\n            prev_prev_zero = prev_zero\n            prev_zero = current\n\n    if length - prev_prev_zero > max_count:\n        max_index = prev_zero\n\n    return max_index\n"
  },
  {
    "path": "algorithms/array/merge_intervals.py",
    "content": "\"\"\"\nMerge Intervals\n\nGiven a collection of intervals, merge all overlapping intervals into a\nconsolidated set.\n\nReference: https://en.wikipedia.org/wiki/Interval_(mathematics)\n\nComplexity:\n    Time:  O(n log n) due to sorting\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Interval:\n    \"\"\"A numeric interval [start, end) with merge and comparison support.\n\n    Args:\n        start: Lower bound of the interval.\n        end: Upper bound of the interval.\n    \"\"\"\n\n    def __init__(self, start: int = 0, end: int = 0) -> None:\n        self.start = start\n        self.end = end\n\n    def __repr__(self) -> str:\n        return f\"Interval ({self.start}, {self.end})\"\n\n    def __iter__(self):\n        return iter(range(self.start, self.end))\n\n    def __getitem__(self, index: int) -> int:\n        if index < 0:\n            return self.end + index\n        return self.start + index\n\n    def __len__(self) -> int:\n        return self.end - self.start\n\n    def __contains__(self, item: int) -> bool:\n        return self.start >= item >= self.end\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Interval):\n            return NotImplemented\n        return self.start == other.start and self.end == other.end\n\n    def as_list(self) -> list[int]:\n        \"\"\"Return interval as a list of integers.\n\n        Returns:\n            List of integers in the interval range.\n        \"\"\"\n        return list(self)\n\n    @staticmethod\n    def merge(intervals: list[Interval]) -> list[Interval]:\n        \"\"\"Merge overlapping intervals into a consolidated list.\n\n        Args:\n            intervals: List of Interval objects to merge.\n\n        Returns:\n            List of merged, non-overlapping Interval objects.\n\n        Examples:\n            >>> Interval.merge([Interval(1, 3), Interval(2, 6)])\n            [Interval (1, 6)]\n        \"\"\"\n        out = []\n        for interval in sorted(intervals, key=lambda i: i.start):\n            if out and interval.start <= out[-1].end:\n                out[-1].end = max(out[-1].end, interval.end)\n            else:\n                out += (interval,)\n        return out\n\n    @staticmethod\n    def print_intervals(intervals: list[Interval]) -> str:\n        \"\"\"Format intervals as a string representation.\n\n        Args:\n            intervals: List of Interval objects to format.\n\n        Returns:\n            String representation of all intervals.\n\n        Examples:\n            >>> Interval.print_intervals([Interval(1, 3)])\n            'Interval (1, 3)'\n        \"\"\"\n        result = []\n        for interval in intervals:\n            result.append(repr(interval))\n        return \"\".join(result)\n\n\ndef merge_intervals(intervals: list[list[int]]) -> list[list[int]] | None:\n    \"\"\"Merge overlapping intervals represented as nested lists.\n\n    Args:\n        intervals: List of [start, end] pairs to merge.\n\n    Returns:\n        List of merged [start, end] pairs, or None if input is None.\n\n    Examples:\n        >>> merge_intervals([[1, 3], [2, 6], [8, 10]])\n        [[1, 6], [8, 10]]\n    \"\"\"\n    if intervals is None:\n        return None\n    intervals.sort(key=lambda i: i[0])\n    out = [intervals.pop(0)]\n    for interval in intervals:\n        if out[-1][-1] >= interval[0]:\n            out[-1][-1] = max(out[-1][-1], interval[-1])\n        else:\n            out.append(interval)\n    return out\n"
  },
  {
    "path": "algorithms/array/missing_ranges.py",
    "content": "\"\"\"\nMissing Ranges\n\nFind the ranges of numbers that are missing between a given low and high\nbound, given a sorted array of integers.\n\nReference: https://leetcode.com/problems/missing-ranges/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) for the result list\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef missing_ranges(array: list[int], low: int, high: int) -> list[tuple[int, int]]:\n    \"\"\"Find gaps between low and high not covered by elements in array.\n\n    Args:\n        array: Sorted list of integers within [low, high].\n        low: Lower bound of the expected range (inclusive).\n        high: Upper bound of the expected range (inclusive).\n\n    Returns:\n        List of (start, end) tuples representing missing ranges.\n\n    Examples:\n        >>> missing_ranges([3, 5], 1, 10)\n        [(1, 2), (4, 4), (6, 10)]\n    \"\"\"\n    result = []\n    start = low\n\n    for num in array:\n        if num == start:\n            start += 1\n        elif num > start:\n            result.append((start, num - 1))\n            start = num + 1\n\n    if start <= high:\n        result.append((start, high))\n\n    return result\n"
  },
  {
    "path": "algorithms/array/move_zeros.py",
    "content": "\"\"\"\nMove Zeros\n\nMove all zeros in an array to the end while preserving the relative order\nof the non-zero (and non-integer-zero) elements.\n\nReference: https://leetcode.com/problems/move-zeroes/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\n\ndef move_zeros(array: list[Any]) -> list[Any]:\n    \"\"\"Move all integer zeros to the end, preserving order of other elements.\n\n    Boolean False is not treated as zero.\n\n    Args:\n        array: Input list with mixed types.\n\n    Returns:\n        New list with all integer 0s moved to the end.\n\n    Examples:\n        >>> move_zeros([False, 1, 0, 1, 2, 0, 1, 3, \"a\"])\n        [False, 1, 1, 2, 1, 3, 'a', 0, 0]\n    \"\"\"\n    result = []\n    zeros = 0\n\n    for element in array:\n        if element == 0 and type(element) is not bool:\n            zeros += 1\n        else:\n            result.append(element)\n\n    result.extend([0] * zeros)\n    return result\n"
  },
  {
    "path": "algorithms/array/n_sum.py",
    "content": "\"\"\"\nN-Sum\n\nGiven an array of integers, find all unique n-tuples that sum to a target\nvalue. Supports custom sum, comparison, and equality closures for advanced\nuse cases with non-integer elements.\n\nReference: https://leetcode.com/problems/4sum/\n\nComplexity:\n    Time:  O(n^(k-1)) where k is the tuple size\n    Space: O(n^(k-1)) for storing results\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Callable\nfrom typing import Any\n\n\ndef n_sum(\n    n: int,\n    nums: list[Any],\n    target: Any,\n    **kv: Callable[..., Any],\n) -> list[list[Any]]:\n    \"\"\"Find all unique n-tuples in nums that sum to target.\n\n    Args:\n        n: Size of each tuple to find.\n        nums: List of elements to search.\n        target: Desired sum for each n-tuple.\n        **kv: Optional closures:\n            sum_closure(a, b) - returns sum of two elements.\n            compare_closure(num, target) - returns -1, 0, or 1.\n            same_closure(a, b) - returns True if elements are equal.\n\n    Returns:\n        Sorted list of unique n-tuples (as lists) that sum to target.\n\n    Examples:\n        >>> n_sum(3, [-1, 0, 1, 2, -1, -4], 0)\n        [[-1, -1, 2], [-1, 0, 1]]\n    \"\"\"\n\n    def _sum_closure_default(a: Any, b: Any) -> Any:\n        return a + b\n\n    def _compare_closure_default(num: Any, target: Any) -> int:\n        if num < target:\n            return -1\n        elif num > target:\n            return 1\n        else:\n            return 0\n\n    def _same_closure_default(a: Any, b: Any) -> bool:\n        return a == b\n\n    def _n_sum_inner(n: int, nums: list[Any], target: Any) -> list[list[Any]]:\n        if n == 2:\n            results = _two_sum(nums, target)\n        else:\n            results = []\n            prev_num = None\n            for index, num in enumerate(nums):\n                if prev_num is not None and same_closure(prev_num, num):\n                    continue\n\n                prev_num = num\n                n_minus1_results = _n_sum_inner(\n                    n - 1,\n                    nums[index + 1 :],\n                    target - num,\n                )\n                n_minus1_results = _append_elem_to_each_list(num, n_minus1_results)\n                results += n_minus1_results\n        return _union(results)\n\n    def _two_sum(nums: list[Any], target: Any) -> list[list[Any]]:\n        nums.sort()\n        left = 0\n        right = len(nums) - 1\n        results = []\n        while left < right:\n            current_sum = sum_closure(nums[left], nums[right])\n            flag = compare_closure(current_sum, target)\n            if flag == -1:\n                left += 1\n            elif flag == 1:\n                right -= 1\n            else:\n                results.append(sorted([nums[left], nums[right]]))\n                left += 1\n                right -= 1\n                while left < len(nums) and same_closure(nums[left - 1], nums[left]):\n                    left += 1\n                while right >= 0 and same_closure(nums[right], nums[right + 1]):\n                    right -= 1\n        return results\n\n    def _append_elem_to_each_list(\n        elem: Any, container: list[list[Any]]\n    ) -> list[list[Any]]:\n        results = []\n        for elems in container:\n            elems.append(elem)\n            results.append(sorted(elems))\n        return results\n\n    def _union(\n        duplicate_results: list[list[Any]],\n    ) -> list[list[Any]]:\n        results = []\n        if len(duplicate_results) != 0:\n            duplicate_results.sort()\n            results.append(duplicate_results[0])\n            for result in duplicate_results[1:]:\n                if results[-1] != result:\n                    results.append(result)\n        return results\n\n    sum_closure = kv.get(\"sum_closure\", _sum_closure_default)\n    same_closure = kv.get(\"same_closure\", _same_closure_default)\n    compare_closure = kv.get(\"compare_closure\", _compare_closure_default)\n    nums.sort()\n    return _n_sum_inner(n, nums, target)\n"
  },
  {
    "path": "algorithms/array/plus_one.py",
    "content": "\"\"\"\nPlus One\n\nGiven a non-negative number represented as an array of digits (big-endian),\nadd one to the number and return the resulting digit array.\n\nReference: https://leetcode.com/problems/plus-one/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) for v1, O(1) auxiliary for v2 and v3\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef plus_one_v1(digits: list[int]) -> list[int]:\n    \"\"\"Add one to a big-endian digit array using manual carry propagation.\n\n    Args:\n        digits: Non-empty list of digits representing a non-negative integer.\n\n    Returns:\n        New list of digits representing the input number plus one.\n\n    Examples:\n        >>> plus_one_v1([1, 2, 9])\n        [1, 3, 0]\n    \"\"\"\n    digits[-1] = digits[-1] + 1\n    result = []\n    carry = 0\n    index = len(digits) - 1\n    while index >= 0 or carry == 1:\n        digit_sum = 0\n        if index >= 0:\n            digit_sum += digits[index]\n        if carry:\n            digit_sum += 1\n        result.append(digit_sum % 10)\n        carry = digit_sum // 10\n        index -= 1\n    return result[::-1]\n\n\ndef plus_one_v2(digits: list[int]) -> list[int]:\n    \"\"\"Add one to a big-endian digit array by scanning from the right.\n\n    Args:\n        digits: Non-empty list of digits representing a non-negative integer.\n\n    Returns:\n        List of digits representing the input number plus one.\n\n    Examples:\n        >>> plus_one_v2([1, 2, 9])\n        [1, 3, 0]\n    \"\"\"\n    length = len(digits)\n    for index in range(length - 1, -1, -1):\n        if digits[index] < 9:\n            digits[index] += 1\n            return digits\n        digits[index] = 0\n    digits.insert(0, 1)\n    return digits\n\n\ndef plus_one_v3(num_arr: list[int]) -> list[int]:\n    \"\"\"Add one to a big-endian digit array using modular arithmetic.\n\n    Args:\n        num_arr: Non-empty list of digits representing a non-negative integer.\n\n    Returns:\n        List of digits representing the input number plus one.\n\n    Examples:\n        >>> plus_one_v3([1, 2, 9])\n        [1, 3, 0]\n    \"\"\"\n    for idx in reversed(list(enumerate(num_arr))):\n        num_arr[idx[0]] = (num_arr[idx[0]] + 1) % 10\n        if num_arr[idx[0]]:\n            return num_arr\n    return [1] + num_arr\n"
  },
  {
    "path": "algorithms/array/remove_duplicates.py",
    "content": "\"\"\"\nRemove Duplicates\n\nRemove duplicate elements from an array while preserving the original order.\nHandles both hashable and unhashable items.\n\nReference: https://en.wikipedia.org/wiki/Duplicate_code\n\nComplexity:\n    Time:  O(n) for hashable items / O(n^2) worst case for unhashable items\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Hashable\nfrom typing import Any\n\n\ndef remove_duplicates(array: list[Any]) -> list[Any]:\n    \"\"\"Remove duplicate elements from an array, preserving order.\n\n    Uses a set for O(1) lookups on hashable items and falls back to\n    linear search for unhashable items.\n\n    Args:\n        array: Input list potentially containing duplicates.\n\n    Returns:\n        New list with duplicates removed, original order preserved.\n\n    Examples:\n        >>> remove_duplicates([1, 1, 2, 2, 3])\n        [1, 2, 3]\n    \"\"\"\n    seen = set()\n    unique_array = []\n\n    for item in array:\n        if isinstance(item, Hashable):\n            if item not in seen:\n                seen.add(item)\n                unique_array.append(item)\n        else:\n            if item not in unique_array:\n                unique_array.append(item)\n\n    return unique_array\n"
  },
  {
    "path": "algorithms/array/rotate.py",
    "content": "\"\"\"\nRotate Array\n\nRotate an array of n elements to the right by k steps.\nThree algorithm variants are provided with different time complexities.\n\nReference: https://leetcode.com/problems/rotate-array/\n\nComplexity:\n    rotate_v1: Time O(n*k), Space O(n)\n    rotate_v2: Time O(n),   Space O(n)\n    rotate_v3: Time O(n),   Space O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef rotate_v1(array: list[int], k: int) -> list[int]:\n    \"\"\"Rotate array to the right by k steps using repeated single shifts.\n\n    Args:\n        array: List of integers to rotate.\n        k: Number of positions to rotate right.\n\n    Returns:\n        New rotated list.\n\n    Examples:\n        >>> rotate_v1([1, 2, 3, 4, 5, 6, 7], 3)\n        [5, 6, 7, 1, 2, 3, 4]\n    \"\"\"\n    array = array[:]\n    length = len(array)\n    for _ in range(k):\n        temp = array[length - 1]\n        for position in range(length - 1, 0, -1):\n            array[position] = array[position - 1]\n        array[0] = temp\n    return array\n\n\ndef rotate_v2(array: list[int], k: int) -> list[int]:\n    \"\"\"Rotate array to the right by k steps using three reversals.\n\n    Args:\n        array: List of integers to rotate.\n        k: Number of positions to rotate right.\n\n    Returns:\n        New rotated list.\n\n    Examples:\n        >>> rotate_v2([1, 2, 3, 4, 5, 6, 7], 3)\n        [5, 6, 7, 1, 2, 3, 4]\n    \"\"\"\n    array = array[:]\n\n    def _reverse(arr: list[int], left: int, right: int) -> None:\n        while left < right:\n            arr[left], arr[right] = arr[right], arr[left]\n            left += 1\n            right -= 1\n\n    length = len(array)\n    k = k % length\n    _reverse(array, 0, length - k - 1)\n    _reverse(array, length - k, length - 1)\n    _reverse(array, 0, length - 1)\n    return array\n\n\ndef rotate_v3(array: list[int] | None, k: int) -> list[int] | None:\n    \"\"\"Rotate array to the right by k steps using slicing.\n\n    Args:\n        array: List of integers to rotate, or None.\n        k: Number of positions to rotate right.\n\n    Returns:\n        New rotated list, or None if input is None.\n\n    Examples:\n        >>> rotate_v3([1, 2, 3, 4, 5, 6, 7], 3)\n        [5, 6, 7, 1, 2, 3, 4]\n    \"\"\"\n    if array is None:\n        return None\n    length = len(array)\n    k = k % length\n    return array[length - k :] + array[: length - k]\n"
  },
  {
    "path": "algorithms/array/summarize_ranges.py",
    "content": "\"\"\"\nSummarize Ranges\n\nGiven a sorted integer array without duplicates, return the summary of its\nranges as a list of (start, end) tuples.\n\nReference: https://leetcode.com/problems/summary-ranges/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) for the result list\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef summarize_ranges(array: list[int]) -> list[tuple[int, ...]]:\n    \"\"\"Summarize consecutive runs in a sorted array as (start, end) tuples.\n\n    Args:\n        array: Sorted list of unique integers.\n\n    Returns:\n        List of (start, end) tuples for each consecutive range.\n\n    Examples:\n        >>> summarize_ranges([0, 1, 2, 4, 5, 7])\n        [(0, 2), (4, 5), (7, 7)]\n    \"\"\"\n    result = []\n    if len(array) == 0:\n        return []\n    if len(array) == 1:\n        return [(array[0], array[0])]\n    iterator = iter(array)\n    start = end = next(iterator)\n    for num in iterator:\n        if num - end == 1:\n            end = num\n        else:\n            result.append((start, end))\n            start = end = num\n    result.append((start, end))\n    return result\n"
  },
  {
    "path": "algorithms/array/three_sum.py",
    "content": "\"\"\"\nThree Sum\n\nGiven an array of integers, find all unique triplets that sum to zero\nusing the two-pointer technique.\n\nReference: https://leetcode.com/problems/3sum/\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n) for the result set\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef three_sum(array: list[int]) -> set[tuple[int, int, int]]:\n    \"\"\"Find all unique triplets in the array that sum to zero.\n\n    Args:\n        array: List of integers to search.\n\n    Returns:\n        Set of sorted tuples, each containing three integers summing to zero.\n\n    Examples:\n        >>> three_sum([-1, 0, 1, 2, -1, -4]) == {(-1, 0, 1), (-1, -1, 2)}\n        True\n    \"\"\"\n    result = set()\n    array.sort()\n    for i in range(len(array) - 2):\n        if i > 0 and array[i] == array[i - 1]:\n            continue\n        left, right = i + 1, len(array) - 1\n        while left < right:\n            current_sum = array[i] + array[left] + array[right]\n            if current_sum > 0:\n                right -= 1\n            elif current_sum < 0:\n                left += 1\n            else:\n                result.add((array[i], array[left], array[right]))\n\n                while left < right and array[left] == array[left + 1]:\n                    left += 1\n\n                while left < right and array[right] == array[right - 1]:\n                    right -= 1\n\n                left += 1\n                right -= 1\n    return result\n"
  },
  {
    "path": "algorithms/array/top_1.py",
    "content": "\"\"\"\nTop 1 (Mode)\n\nFind the most frequently occurring value(s) in an array. When multiple\nvalues share the highest frequency, all are returned.\n\nReference: https://en.wikipedia.org/wiki/Mode_(statistics)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\n\ndef top_1(array: list[Any]) -> list[Any]:\n    \"\"\"Find the statistical mode(s) of an array.\n\n    Args:\n        array: Input list of comparable elements.\n\n    Returns:\n        List of element(s) with the highest frequency.\n\n    Examples:\n        >>> top_1([1, 1, 2, 2, 3])\n        [1, 2]\n    \"\"\"\n    frequency = {}\n    for element in array:\n        if element in frequency:\n            frequency[element] += 1\n        else:\n            frequency[element] = 1\n\n    max_count = max(frequency.values())\n\n    result = []\n    for element, count in frequency.items():\n        if count == max_count:\n            result.append(element)\n\n    return result\n"
  },
  {
    "path": "algorithms/array/trimmean.py",
    "content": "\"\"\"\nTrimmed Mean\n\nCompute the mean of an array after discarding a given percentage of the\nhighest and lowest values. Useful for robust averaging in scoring systems.\n\nReference: https://en.wikipedia.org/wiki/Truncated_mean\n\nComplexity:\n    Time:  O(n log n) due to sorting\n    Space: O(n) for the trimmed copy\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef trimmean(array: list[float], percentage: float) -> float:\n    \"\"\"Calculate the trimmed mean of an array.\n\n    Discards the top and bottom halves of the given percentage before\n    computing the arithmetic mean.\n\n    Args:\n        array: List of numeric values.\n        percentage: Total percentage to trim (split equally between\n            top and bottom). E.g., 20 trims the top 10% and bottom 10%.\n\n    Returns:\n        The trimmed arithmetic mean.\n\n    Examples:\n        >>> trimmean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 20)\n        5.5\n    \"\"\"\n    ratio = percentage / 200\n    array.sort()\n    trim_count = int(len(array) * ratio)\n    trimmed = array[trim_count : len(array) - trim_count]\n    total = 0\n    for value in trimmed:\n        total += value\n    return total / len(trimmed)\n"
  },
  {
    "path": "algorithms/array/two_sum.py",
    "content": "\"\"\"\nTwo Sum\n\nGiven an array of integers and a target sum, return the indices of the two\nnumbers that add up to the target.\n\nReference: https://leetcode.com/problems/two-sum/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef two_sum(array: list[int], target: int) -> tuple[int, int] | None:\n    \"\"\"Find two indices whose corresponding values sum to target.\n\n    Args:\n        array: List of integers to search.\n        target: Desired sum of two elements.\n\n    Returns:\n        Tuple of (index1, index2) if a pair is found, or None otherwise.\n\n    Examples:\n        >>> two_sum([2, 7, 11, 15], 9)\n        (0, 1)\n    \"\"\"\n    seen = {}\n    for index, num in enumerate(array):\n        if num in seen:\n            return seen[num], index\n        else:\n            seen[target - num] = index\n    return None\n"
  },
  {
    "path": "algorithms/backtracking/__init__.py",
    "content": "from .add_operators import add_operators\nfrom .anagram import anagram\nfrom .array_sum_combinations import (\n    array_sum_combinations,\n    unique_array_sum_combinations,\n)\nfrom .combination_sum import combination_sum\nfrom .factor_combinations import get_factors, recursive_get_factors\nfrom .find_words import find_words\nfrom .generate_abbreviations import generate_abbreviations\nfrom .generate_parenthesis import generate_parenthesis_v1, generate_parenthesis_v2\nfrom .letter_combination import letter_combinations\nfrom .minimax import minimax\nfrom .palindrome_partitioning import (\n    palindromic_substrings,\n    palindromic_substrings_iter,\n)\nfrom .pattern_match import pattern_match\nfrom .permute import permute, permute_iter, permute_recursive\nfrom .permute_unique import permute_unique\nfrom .subsets import subsets, subsets_v2\nfrom .subsets_unique import subsets_unique\n\n__all__ = [\n    \"add_operators\",\n    \"anagram\",\n    \"array_sum_combinations\",\n    \"unique_array_sum_combinations\",\n    \"combination_sum\",\n    \"get_factors\",\n    \"recursive_get_factors\",\n    \"find_words\",\n    \"generate_abbreviations\",\n    \"generate_parenthesis_v1\",\n    \"generate_parenthesis_v2\",\n    \"letter_combinations\",\n    \"palindromic_substrings\",\n    \"palindromic_substrings_iter\",\n    \"pattern_match\",\n    \"permute\",\n    \"permute_iter\",\n    \"permute_recursive\",\n    \"permute_unique\",\n    \"subsets\",\n    \"subsets_v2\",\n    \"subsets_unique\",\n    \"minimax\",\n]\n"
  },
  {
    "path": "algorithms/backtracking/add_operators.py",
    "content": "\"\"\"\nExpression Add Operators\n\nGiven a string of digits and a target value, return all possibilities to\ninsert binary operators (+, -, *) between the digits so they evaluate to\nthe target value.\n\nReference: https://leetcode.com/problems/expression-add-operators/\n\nComplexity:\n    Time:  O(4^n) worst\n    Space: O(n) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef add_operators(digits: str, target: int) -> list[str]:\n    \"\"\"Return all expressions formed by inserting +, -, * that equal target.\n\n    Args:\n        digits: A string containing only digits 0-9.\n        target: The target integer value.\n\n    Returns:\n        A list of valid expression strings.\n\n    Examples:\n        >>> add_operators(\"123\", 6)\n        ['1+2+3', '1*2*3']\n    \"\"\"\n    result: list[str] = []\n    if not digits:\n        return result\n    _dfs(result, \"\", digits, target, 0, 0, 0)\n    return result\n\n\ndef _dfs(\n    result: list[str],\n    path: str,\n    digits: str,\n    target: int,\n    position: int,\n    evaluated: int,\n    multed: int,\n) -> None:\n    \"\"\"Depth-first search helper that builds expressions recursively.\"\"\"\n    if position == len(digits):\n        if target == evaluated:\n            result.append(path)\n        return\n    for i in range(position, len(digits)):\n        if i != position and digits[position] == \"0\":\n            break\n        current = int(digits[position : i + 1])\n        if position == 0:\n            _dfs(result, path + str(current), digits, target, i + 1, current, current)\n        else:\n            _dfs(\n                result,\n                path + \"+\" + str(current),\n                digits,\n                target,\n                i + 1,\n                evaluated + current,\n                current,\n            )\n            _dfs(\n                result,\n                path + \"-\" + str(current),\n                digits,\n                target,\n                i + 1,\n                evaluated - current,\n                -current,\n            )\n            _dfs(\n                result,\n                path + \"*\" + str(current),\n                digits,\n                target,\n                i + 1,\n                evaluated - multed + multed * current,\n                multed * current,\n            )\n"
  },
  {
    "path": "algorithms/backtracking/anagram.py",
    "content": "\"\"\"\nAnagram Checker\n\nGiven two strings, determine if they are anagrams of each other (i.e. one\ncan be rearranged to form the other).\n\nReference: https://en.wikipedia.org/wiki/Anagram\n\nComplexity:\n    Time:  O(n) where n is the length of the strings\n    Space: O(1) fixed 26-character alphabet\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef anagram(first: str, second: str) -> bool:\n    \"\"\"Check whether two strings are anagrams of each other.\n\n    Args:\n        first: The first string (lowercase letters only).\n        second: The second string (lowercase letters only).\n\n    Returns:\n        True if the strings are anagrams, False otherwise.\n\n    Examples:\n        >>> anagram('apple', 'pleap')\n        True\n        >>> anagram('apple', 'cherry')\n        False\n    \"\"\"\n    count_first = [0] * 26\n    count_second = [0] * 26\n\n    for char in first:\n        index = ord(char) - ord(\"a\")\n        count_first[index] += 1\n\n    for char in second:\n        index = ord(char) - ord(\"a\")\n        count_second[index] += 1\n\n    return count_first == count_second\n"
  },
  {
    "path": "algorithms/backtracking/array_sum_combinations.py",
    "content": "\"\"\"\nArray Sum Combinations\n\nGiven three arrays and a target sum, find all three-element combinations\n(one element from each array) that add up to the target.\n\nReference: https://en.wikipedia.org/wiki/Subset_sum_problem\n\nComplexity:\n    Time:  O(n^3) brute-force product of three arrays\n    Space: O(k) where k is the number of valid combinations\n\"\"\"\n\nfrom __future__ import annotations\n\nimport itertools\nfrom functools import partial\n\n\ndef array_sum_combinations(\n    array_a: list[int],\n    array_b: list[int],\n    array_c: list[int],\n    target: int,\n) -> list[list[int]]:\n    \"\"\"Find all combinations of one element per array that sum to target.\n\n    Uses backtracking to enumerate valid combinations, allowing duplicates.\n\n    Args:\n        array_a: First array of integers.\n        array_b: Second array of integers.\n        array_c: Third array of integers.\n        target: The desired sum.\n\n    Returns:\n        A list of three-element lists that sum to target.\n\n    Examples:\n        >>> array_sum_combinations([1], [2], [3], 6)\n        [[1, 2, 3]]\n    \"\"\"\n    arrays = [array_a, array_b, array_c]\n\n    def _is_complete(constructed_so_far: list[int]) -> tuple[bool, bool]:\n        total = sum(constructed_so_far)\n        should_stop = total >= target or len(constructed_so_far) >= 3\n        reached_target = total == target and len(constructed_so_far) == 3\n        return should_stop, reached_target\n\n    def _get_candidates(constructed_so_far: list[int]) -> list[int]:\n        return arrays[len(constructed_so_far)]\n\n    def _backtrack(\n        constructed_so_far: list[int] | None = None,\n        result: list[list[int]] | None = None,\n    ) -> None:\n        if constructed_so_far is None:\n            constructed_so_far = []\n        if result is None:\n            result = []\n        should_stop, reached_target = _is_complete(constructed_so_far)\n        if should_stop:\n            if reached_target:\n                result.append(constructed_so_far)\n            return\n        candidates = _get_candidates(constructed_so_far)\n        for candidate in candidates:\n            constructed_so_far.append(candidate)\n            _backtrack(constructed_so_far[:], result)\n            constructed_so_far.pop()\n\n    result: list[list[int]] = []\n    _backtrack([], result)\n    return result\n\n\ndef unique_array_sum_combinations(\n    array_a: list[int],\n    array_b: list[int],\n    array_c: list[int],\n    target: int,\n) -> list[tuple[int, ...]]:\n    \"\"\"Find unique combinations of one element per array that sum to target.\n\n    Uses itertools.product and filters by target sum, returning only unique\n    tuples.\n\n    Args:\n        array_a: First array of integers.\n        array_b: Second array of integers.\n        array_c: Third array of integers.\n        target: The desired sum.\n\n    Returns:\n        A list of unique tuples that sum to target.\n\n    Examples:\n        >>> sorted(unique_array_sum_combinations([1, 2], [2, 3], [3, 4], 6))\n        [(1, 2, 3)]\n    \"\"\"\n\n    def _check_sum(expected: int, *nums: int) -> tuple[bool, tuple[int, ...]]:\n        if sum(nums) == expected:\n            return (True, nums)\n        else:\n            return (False, nums)\n\n    product = itertools.product(array_a, array_b, array_c)\n    func = partial(_check_sum, target)\n    sums = list(itertools.starmap(func, product))\n\n    seen: set[tuple[int, ...]] = set()\n    for is_match, values in sums:\n        if is_match and values not in seen:\n            seen.add(values)\n\n    return list(seen)\n"
  },
  {
    "path": "algorithms/backtracking/combination_sum.py",
    "content": "\"\"\"\nCombination Sum\n\nGiven a set of candidate numbers (without duplicates) and a target number,\nfind all unique combinations where the candidate numbers sum to the target.\nThe same number may be chosen an unlimited number of times.\n\nReference: https://leetcode.com/problems/combination-sum/\n\nComplexity:\n    Time:  O(n^(T/M)) where T is target, M is minimum candidate\n    Space: O(T/M) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef combination_sum(candidates: list[int], target: int) -> list[list[int]]:\n    \"\"\"Find all unique combinations of candidates that sum to target.\n\n    Args:\n        candidates: A list of distinct positive integers.\n        target: The target sum.\n\n    Returns:\n        A list of lists, each containing a valid combination.\n\n    Examples:\n        >>> combination_sum([2, 3, 6, 7], 7)\n        [[2, 2, 3], [7]]\n    \"\"\"\n    result: list[list[int]] = []\n    candidates.sort()\n    _dfs(candidates, target, 0, [], result)\n    return result\n\n\ndef _dfs(\n    nums: list[int],\n    target: int,\n    index: int,\n    path: list[int],\n    result: list[list[int]],\n) -> None:\n    \"\"\"Depth-first search helper for building combinations.\"\"\"\n    if target < 0:\n        return\n    if target == 0:\n        result.append(path)\n        return\n    for i in range(index, len(nums)):\n        _dfs(nums, target - nums[i], i, path + [nums[i]], result)\n"
  },
  {
    "path": "algorithms/backtracking/factor_combinations.py",
    "content": "\"\"\"\nFactor Combinations\n\nGiven an integer n, return all possible combinations of its factors.\nFactors should be greater than 1 and less than n.\n\nReference: https://leetcode.com/problems/factor-combinations/\n\nComplexity:\n    Time:  O(n * log(n)) approximate\n    Space: O(log(n)) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef get_factors(number: int) -> list[list[int]]:\n    \"\"\"Return all factor combinations of number using iteration.\n\n    Args:\n        number: A positive integer.\n\n    Returns:\n        A list of lists, each containing a valid factorization.\n\n    Examples:\n        >>> get_factors(12)\n        [[2, 6], [2, 2, 3], [3, 4]]\n    \"\"\"\n    todo: list[tuple[int, int, list[int]]] = [(number, 2, [])]\n    combinations: list[list[int]] = []\n    while todo:\n        remaining, divisor, partial = todo.pop()\n        while divisor * divisor <= remaining:\n            if remaining % divisor == 0:\n                combinations.append(partial + [divisor, remaining // divisor])\n                todo.append((remaining // divisor, divisor, partial + [divisor]))\n            divisor += 1\n    return combinations\n\n\ndef recursive_get_factors(number: int) -> list[list[int]]:\n    \"\"\"Return all factor combinations of number using recursion.\n\n    Args:\n        number: A positive integer.\n\n    Returns:\n        A list of lists, each containing a valid factorization.\n\n    Examples:\n        >>> recursive_get_factors(12)\n        [[2, 6], [2, 2, 3], [3, 4]]\n    \"\"\"\n\n    def _factor(\n        remaining: int,\n        divisor: int,\n        partial: list[int],\n        combinations: list[list[int]],\n    ) -> list[list[int]]:\n        while divisor * divisor <= remaining:\n            if remaining % divisor == 0:\n                combinations.append(partial + [divisor, remaining // divisor])\n                _factor(\n                    remaining // divisor, divisor, partial + [divisor], combinations\n                )\n            divisor += 1\n        return combinations\n\n    return _factor(number, 2, [], [])\n"
  },
  {
    "path": "algorithms/backtracking/find_words.py",
    "content": "\"\"\"\nWord Search II\n\nGiven a board of characters and a list of words, find all words that can\nbe constructed from adjacent cells (horizontally or vertically). Each cell\nmay only be used once per word. Uses a trie for efficient prefix matching.\n\nReference: https://leetcode.com/problems/word-search-ii/\n\nComplexity:\n    Time:  O(M * N * 4^L) where M*N is board size, L is max word length\n    Space: O(W * L) for the trie, where W is number of words\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_words(board: list[list[str]], words: list[str]) -> list[str]:\n    \"\"\"Find all words from the list that exist on the board.\n\n    Builds a trie from the word list, then uses backtracking to search\n    the board for each word.\n\n    Args:\n        board: A 2D grid of characters.\n        words: A list of words to search for.\n\n    Returns:\n        A list of words found on the board.\n\n    Examples:\n        >>> board = [['o','a','a','n'], ['e','t','a','e']]\n        >>> sorted(find_words(board, ['eat', 'oath']))\n        ['eat']\n    \"\"\"\n    trie: dict = {}\n    for word in words:\n        current_node = trie\n        for char in word:\n            if char not in current_node:\n                current_node[char] = {}\n            current_node = current_node[char]\n        current_node[\"#\"] = \"#\"\n\n    found: set[str] = set()\n    used = [[False] * len(board[0]) for _ in range(len(board))] if board else []\n\n    for row in range(len(board)):\n        for col in range(len(board[0])):\n            _backtrack(board, row, col, trie, \"\", used, found)\n\n    return list(found)\n\n\ndef _backtrack(\n    board: list[list[str]],\n    row: int,\n    col: int,\n    trie: dict,\n    prefix: str,\n    used: list[list[bool]],\n    found: set[str],\n) -> None:\n    \"\"\"Recursively search the board for words matching the trie.\"\"\"\n    if \"#\" in trie:\n        found.add(prefix)\n\n    if row < 0 or row >= len(board) or col < 0 or col >= len(board[0]):\n        return\n\n    if not used[row][col] and board[row][col] in trie:\n        used[row][col] = True\n        next_char = board[row][col]\n        _backtrack(\n            board, row + 1, col, trie[next_char], prefix + next_char, used, found\n        )\n        _backtrack(\n            board, row, col + 1, trie[next_char], prefix + next_char, used, found\n        )\n        _backtrack(\n            board, row - 1, col, trie[next_char], prefix + next_char, used, found\n        )\n        _backtrack(\n            board, row, col - 1, trie[next_char], prefix + next_char, used, found\n        )\n        used[row][col] = False\n"
  },
  {
    "path": "algorithms/backtracking/generate_abbreviations.py",
    "content": "\"\"\"\nGeneralized Abbreviations\n\nGiven a word, return all possible generalized abbreviations. Each\nabbreviation replaces contiguous substrings with their lengths.\n\nReference: https://leetcode.com/problems/generalized-abbreviation/\n\nComplexity:\n    Time:  O(2^n) where n is the length of the word\n    Space: O(n) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef generate_abbreviations(word: str) -> list[str]:\n    \"\"\"Generate all possible abbreviations of a word.\n\n    Args:\n        word: The input word to abbreviate.\n\n    Returns:\n        A list of all valid abbreviations.\n\n    Examples:\n        >>> sorted(generate_abbreviations(\"ab\"))\n        ['1b', '2', 'a1', 'ab']\n    \"\"\"\n    result: list[str] = []\n    _backtrack(result, word, 0, 0, \"\")\n    return result\n\n\ndef _backtrack(\n    result: list[str],\n    word: str,\n    position: int,\n    count: int,\n    current: str,\n) -> None:\n    \"\"\"Recursively build abbreviations by including or skipping characters.\"\"\"\n    if position == len(word):\n        if count > 0:\n            current += str(count)\n        result.append(current)\n        return\n\n    if count > 0:\n        _backtrack(result, word, position + 1, 0, current + str(count) + word[position])\n    else:\n        _backtrack(result, word, position + 1, 0, current + word[position])\n    _backtrack(result, word, position + 1, count + 1, current)\n"
  },
  {
    "path": "algorithms/backtracking/generate_parenthesis.py",
    "content": "\"\"\"\nGenerate Parentheses\n\nGiven n pairs of parentheses, generate all combinations of well-formed\nparentheses.\n\nReference: https://leetcode.com/problems/generate-parentheses/\n\nComplexity:\n    Time:  O(4^n / sqrt(n)) — the n-th Catalan number\n    Space: O(n) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef generate_parenthesis_v1(count: int) -> list[str]:\n    \"\"\"Generate all valid parenthesis combinations (right-first variant).\n\n    Builds combinations by tracking remaining left and right parentheses,\n    trying to add a closing paren before an opening one.\n\n    Args:\n        count: The number of parenthesis pairs.\n\n    Returns:\n        A list of all valid parenthesis strings.\n\n    Examples:\n        >>> generate_parenthesis_v1(2)\n        ['()()', '(())']\n    \"\"\"\n    result: list[str] = []\n    _add_pair_v1(result, \"\", count, 0)\n    return result\n\n\ndef _add_pair_v1(\n    result: list[str],\n    current: str,\n    left: int,\n    right: int,\n) -> None:\n    \"\"\"Recursive helper for v1: tries closing before opening.\"\"\"\n    if left == 0 and right == 0:\n        result.append(current)\n        return\n    if right > 0:\n        _add_pair_v1(result, current + \")\", left, right - 1)\n    if left > 0:\n        _add_pair_v1(result, current + \"(\", left - 1, right + 1)\n\n\ndef generate_parenthesis_v2(count: int) -> list[str]:\n    \"\"\"Generate all valid parenthesis combinations (left-first variant).\n\n    Builds combinations by tracking remaining left and right parentheses,\n    trying to add an opening paren before a closing one.\n\n    Args:\n        count: The number of parenthesis pairs.\n\n    Returns:\n        A list of all valid parenthesis strings.\n\n    Examples:\n        >>> generate_parenthesis_v2(2)\n        ['(())', '()()']\n    \"\"\"\n    result: list[str] = []\n    _add_pair_v2(result, \"\", count, count)\n    return result\n\n\ndef _add_pair_v2(\n    result: list[str],\n    current: str,\n    left: int,\n    right: int,\n) -> None:\n    \"\"\"Recursive helper for v2: tries opening before closing.\"\"\"\n    if left == 0 and right == 0:\n        result.append(current)\n    if left > 0:\n        _add_pair_v2(result, current + \"(\", left - 1, right)\n    if right > 0 and left < right:\n        _add_pair_v2(result, current + \")\", left, right - 1)\n"
  },
  {
    "path": "algorithms/backtracking/letter_combination.py",
    "content": "\"\"\"\nLetter Combinations of a Phone Number\n\nGiven a digit string, return all possible letter combinations that the\nnumber could represent using a telephone keypad mapping.\n\nReference: https://leetcode.com/problems/letter-combinations-of-a-phone-number/\n\nComplexity:\n    Time:  O(4^n) where n is the number of digits\n    Space: O(4^n) for the result list\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef letter_combinations(digits: str) -> list[str]:\n    \"\"\"Return all letter combinations for a digit string.\n\n    Args:\n        digits: A string of digits (2-9).\n\n    Returns:\n        A list of all possible letter combinations.\n\n    Examples:\n        >>> letter_combinations(\"23\")\n        ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']\n    \"\"\"\n    if digits == \"\":\n        return []\n    keypad_map = {\n        \"2\": \"abc\",\n        \"3\": \"def\",\n        \"4\": \"ghi\",\n        \"5\": \"jkl\",\n        \"6\": \"mno\",\n        \"7\": \"pqrs\",\n        \"8\": \"tuv\",\n        \"9\": \"wxyz\",\n    }\n    combinations: list[str] = [\"\"]\n    for digit in digits:\n        expanded: list[str] = []\n        for existing in combinations:\n            for char in keypad_map[digit]:\n                expanded.append(existing + char)\n        combinations = expanded\n    return combinations\n"
  },
  {
    "path": "algorithms/backtracking/minimax.py",
    "content": "\"\"\"Minimax — game-tree search with alpha-beta pruning.\n\nThe minimax algorithm finds the optimal move for a two-player zero-sum\ngame. Alpha-beta pruning reduces the search space by eliminating branches\nthat cannot influence the final decision.\n\nInspired by PR #860 (DD2480-group16).\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef minimax(\n    depth: int,\n    is_maximizing: bool,\n    scores: list[int],\n    alpha: float = -math.inf,\n    beta: float = math.inf,\n) -> float:\n    \"\"\"Return the minimax value of a perfect binary game tree.\n\n    *scores* contains the leaf values (length must be a power of 2).\n    *depth* is the current depth (start with log2(len(scores))).\n\n    >>> minimax(2, True, [3, 5, 2, 9])\n    5\n    >>> minimax(3, True, [3, 5, 2, 9, 12, 5, 23, 23])\n    12\n    \"\"\"\n    if depth == 0:\n        return scores[0]\n    mid = len(scores) // 2\n    if is_maximizing:\n        value = -math.inf\n        value = max(\n            value,\n            minimax(depth - 1, False, scores[:mid], alpha, beta),\n        )\n        alpha = max(alpha, value)\n        if alpha < beta:\n            value = max(\n                value,\n                minimax(depth - 1, False, scores[mid:], alpha, beta),\n            )\n        return value\n    else:\n        value = math.inf\n        value = min(\n            value,\n            minimax(depth - 1, True, scores[:mid], alpha, beta),\n        )\n        beta = min(beta, value)\n        if alpha < beta:\n            value = min(\n                value,\n                minimax(depth - 1, True, scores[mid:], alpha, beta),\n            )\n        return value\n"
  },
  {
    "path": "algorithms/backtracking/palindrome_partitioning.py",
    "content": "\"\"\"\nPalindrome Partitioning\n\nGiven a string, find all ways to partition it into palindromic substrings.\nThere is always at least one way since single characters are palindromes.\n\nReference: https://leetcode.com/problems/palindrome-partitioning/\n\nComplexity:\n    Time:  O(n * 2^n) where n is the string length\n    Space: O(n) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Generator\n\n\ndef palindromic_substrings(text: str) -> list[list[str]]:\n    \"\"\"Return all palindrome partitions of the input string.\n\n    Args:\n        text: The string to partition.\n\n    Returns:\n        A list of partitions, each being a list of palindromic substrings.\n\n    Examples:\n        >>> palindromic_substrings(\"abc\")\n        [['a', 'b', 'c']]\n    \"\"\"\n    if not text:\n        return [[]]\n    results: list[list[str]] = []\n    for i in range(len(text), 0, -1):\n        substring = text[:i]\n        if substring == substring[::-1]:\n            for rest in palindromic_substrings(text[i:]):\n                results.append([substring] + rest)\n    return results\n\n\ndef palindromic_substrings_iter(text: str) -> Generator[list[str], None, None]:\n    \"\"\"Yield all palindrome partitions of the input string via a generator.\n\n    A slightly more Pythonic approach using a recursive generator.\n\n    Args:\n        text: The string to partition.\n\n    Yields:\n        Lists of palindromic substrings forming a valid partition.\n\n    Examples:\n        >>> list(palindromic_substrings_iter(\"abc\"))\n        [['a', 'b', 'c']]\n    \"\"\"\n    if not text:\n        yield []\n        return\n    for i in range(len(text), 0, -1):\n        substring = text[:i]\n        if substring == substring[::-1]:\n            for rest in palindromic_substrings_iter(text[i:]):\n                yield [substring] + rest\n"
  },
  {
    "path": "algorithms/backtracking/pattern_match.py",
    "content": "\"\"\"\nPattern Matching\n\nGiven a pattern and a string, determine if the string follows the same\npattern. A full match means a bijection between each letter in the pattern\nand a non-empty substring in the string.\n\nReference: https://leetcode.com/problems/word-pattern-ii/\n\nComplexity:\n    Time:  O(n^m) where n is string length, m is pattern length\n    Space: O(m) recursion depth plus mapping storage\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef pattern_match(pattern: str, string: str) -> bool:\n    \"\"\"Check whether a string matches a given pattern via bijection.\n\n    Args:\n        pattern: A pattern string of lowercase letters.\n        string: The string to match against (lowercase letters).\n\n    Returns:\n        True if the string follows the pattern, False otherwise.\n\n    Examples:\n        >>> pattern_match(\"abab\", \"redblueredblue\")\n        True\n        >>> pattern_match(\"aabb\", \"xyzabcxzyabc\")\n        False\n    \"\"\"\n    return _backtrack(pattern, string, {})\n\n\ndef _backtrack(\n    pattern: str,\n    string: str,\n    mapping: dict[str, str],\n) -> bool:\n    \"\"\"Recursively attempt to match pattern to string using a mapping.\"\"\"\n    if len(pattern) == 0 and len(string) > 0:\n        return False\n\n    if len(pattern) == 0 and len(string) == 0:\n        return True\n\n    for end in range(1, len(string) - len(pattern) + 2):\n        if pattern[0] not in mapping and string[:end] not in mapping.values():\n            mapping[pattern[0]] = string[:end]\n            if _backtrack(pattern[1:], string[end:], mapping):\n                return True\n            del mapping[pattern[0]]\n        elif pattern[0] in mapping and mapping[pattern[0]] == string[:end]:\n            if _backtrack(pattern[1:], string[end:], mapping):\n                return True\n    return False\n"
  },
  {
    "path": "algorithms/backtracking/permute.py",
    "content": "\"\"\"\nPermutations\n\nGiven a collection of distinct elements, return all possible permutations.\n\nReference: https://en.wikipedia.org/wiki/Permutation\n\nComplexity:\n    Time:  O(n * n!) where n is the number of elements\n    Space: O(n * n!) to store all permutations\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Generator\n\n\ndef permute(elements: list | str) -> list:\n    \"\"\"Return all permutations of the given elements.\n\n    Args:\n        elements: A list or string of distinct elements.\n\n    Returns:\n        A list of all permutations (same type as input elements).\n\n    Examples:\n        >>> permute([1, 2, 3])\n        [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]\n    \"\"\"\n    if len(elements) <= 1:\n        return [elements]\n    result = []\n    for perm in permute(elements[1:]):\n        for i in range(len(elements)):\n            result.append(perm[:i] + elements[0:1] + perm[i:])\n    return result\n\n\ndef permute_iter(elements: list | str) -> Generator:\n    \"\"\"Yield all permutations of the given elements one at a time.\n\n    Args:\n        elements: A list or string of distinct elements.\n\n    Yields:\n        One permutation at a time (same type as input elements).\n\n    Examples:\n        >>> list(permute_iter([1, 2]))\n        [[1, 2], [2, 1]]\n    \"\"\"\n    if len(elements) <= 1:\n        yield elements\n    else:\n        for perm in permute_iter(elements[1:]):\n            for i in range(len(elements)):\n                yield perm[:i] + elements[0:1] + perm[i:]\n\n\ndef permute_recursive(nums: list[int]) -> list[list[int]]:\n    \"\"\"Return all permutations using DFS backtracking.\n\n    Args:\n        nums: A list of distinct integers.\n\n    Returns:\n        A list of all permutations.\n\n    Examples:\n        >>> sorted(permute_recursive([1, 2]))\n        [[1, 2], [2, 1]]\n    \"\"\"\n    result: list[list[int]] = []\n    _dfs(result, nums, [])\n    return result\n\n\ndef _dfs(\n    result: list[list[int]],\n    nums: list[int],\n    path: list[int],\n) -> None:\n    \"\"\"DFS helper that builds permutations by choosing remaining elements.\"\"\"\n    if not nums:\n        result.append(path)\n    for i in range(len(nums)):\n        _dfs(result, nums[:i] + nums[i + 1 :], path + [nums[i]])\n"
  },
  {
    "path": "algorithms/backtracking/permute_unique.py",
    "content": "\"\"\"\nUnique Permutations\n\nGiven a collection of numbers that might contain duplicates, return all\npossible unique permutations.\n\nReference: https://leetcode.com/problems/permutations-ii/\n\nComplexity:\n    Time:  O(n * n!) worst case\n    Space: O(n * n!) to store all unique permutations\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef permute_unique(nums: list[int]) -> list[list[int]]:\n    \"\"\"Return all unique permutations of a list that may have duplicates.\n\n    Args:\n        nums: A list of integers, possibly with duplicates.\n\n    Returns:\n        A list of all unique permutations.\n\n    Examples:\n        >>> sorted(permute_unique([1, 1, 2]))\n        [[1, 1, 2], [1, 2, 1], [2, 1, 1]]\n    \"\"\"\n    permutations: list[list[int]] = [[]]\n    for number in nums:\n        new_permutations: list[list[int]] = []\n        for existing in permutations:\n            for i in range(len(existing) + 1):\n                new_permutations.append(existing[:i] + [number] + existing[i:])\n                if i < len(existing) and existing[i] == number:\n                    break\n        permutations = new_permutations\n    return permutations\n"
  },
  {
    "path": "algorithms/backtracking/subsets.py",
    "content": "\"\"\"\nSubsets\n\nGiven a set of distinct integers, return all possible subsets (the power\nset). The solution set must not contain duplicate subsets.\n\nReference: https://en.wikipedia.org/wiki/Power_set\n\nComplexity:\n    Time:  O(2^n) where n is the number of elements\n    Space: O(2^n) to store all subsets\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef subsets(nums: list[int]) -> list[list[int]]:\n    \"\"\"Return all subsets of the given list using backtracking.\n\n    Args:\n        nums: A list of distinct integers.\n\n    Returns:\n        A list of all subsets.\n\n    Examples:\n        >>> sorted(subsets([1, 2]), key=str)\n        [[], [1], [1, 2], [2]]\n    \"\"\"\n    result: list[list[int]] = []\n    _backtrack(result, nums, [], 0)\n    return result\n\n\ndef _backtrack(\n    result: list[list[int]],\n    nums: list[int],\n    stack: list[int],\n    position: int,\n) -> None:\n    \"\"\"Recursive helper that includes or excludes each element.\"\"\"\n    if position == len(nums):\n        result.append(list(stack))\n    else:\n        stack.append(nums[position])\n        _backtrack(result, nums, stack, position + 1)\n        stack.pop()\n        _backtrack(result, nums, stack, position + 1)\n\n\ndef subsets_v2(nums: list[int]) -> list[list[int]]:\n    \"\"\"Return all subsets of the given list using iteration.\n\n    Builds subsets iteratively by extending each existing subset\n    with the next element.\n\n    Args:\n        nums: A list of distinct integers.\n\n    Returns:\n        A list of all subsets.\n\n    Examples:\n        >>> sorted(subsets_v2([1, 2]), key=str)\n        [[], [1], [1, 2], [2]]\n    \"\"\"\n    result: list[list[int]] = [[]]\n    for number in sorted(nums):\n        result += [item + [number] for item in result]\n    return result\n"
  },
  {
    "path": "algorithms/backtracking/subsets_unique.py",
    "content": "\"\"\"\nUnique Subsets\n\nGiven a collection of integers that might contain duplicates, return all\npossible unique subsets (the power set without duplicates).\n\nReference: https://leetcode.com/problems/subsets-ii/\n\nComplexity:\n    Time:  O(2^n) where n is the number of elements\n    Space: O(2^n) to store all subsets\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef subsets_unique(nums: list[int]) -> list[tuple[int, ...]]:\n    \"\"\"Return all unique subsets of a list that may have duplicates.\n\n    Args:\n        nums: A list of integers, possibly with duplicates.\n\n    Returns:\n        A list of unique tuples representing each subset.\n\n    Examples:\n        >>> sorted(subsets_unique([1, 2, 2]))\n        [(), (1,), (1, 2), (1, 2, 2), (2,), (2, 2)]\n    \"\"\"\n    found: set[tuple[int, ...]] = set()\n    _backtrack(found, nums, [], 0)\n    return list(found)\n\n\ndef _backtrack(\n    found: set[tuple[int, ...]],\n    nums: list[int],\n    stack: list[int],\n    position: int,\n) -> None:\n    \"\"\"Recursive helper that includes or excludes each element.\"\"\"\n    if position == len(nums):\n        found.add(tuple(stack))\n    else:\n        stack.append(nums[position])\n        _backtrack(found, nums, stack, position + 1)\n        stack.pop()\n        _backtrack(found, nums, stack, position + 1)\n"
  },
  {
    "path": "algorithms/bit_manipulation/__init__.py",
    "content": "from .add_bitwise_operator import add_bitwise_operator\nfrom .binary_gap import binary_gap\nfrom .bit_operation import clear_bit, get_bit, set_bit, update_bit\nfrom .bytes_int_conversion import (\n    bytes_big_endian_to_int,\n    bytes_little_endian_to_int,\n    int_to_bytes_big_endian,\n    int_to_bytes_little_endian,\n)\nfrom .count_flips_to_convert import count_flips_to_convert\nfrom .count_ones import count_ones_iter, count_ones_recur\nfrom .find_difference import find_difference\nfrom .find_missing_number import find_missing_number, find_missing_number2\nfrom .flip_bit_longest_sequence import flip_bit_longest_seq\nfrom .gray_code import gray_code, gray_to_binary\nfrom .has_alternative_bit import has_alternative_bit, has_alternative_bit_fast\nfrom .insert_bit import insert_mult_bits, insert_one_bit\nfrom .power_of_two import is_power_of_two\nfrom .remove_bit import remove_bit\nfrom .reverse_bits import reverse_bits\nfrom .single_number import single_number\nfrom .single_number2 import single_number2\nfrom .single_number3 import single_number3\nfrom .subsets import subsets\nfrom .swap_pair import swap_pair\n\n__all__ = [\n    \"add_bitwise_operator\",\n    \"binary_gap\",\n    \"bytes_big_endian_to_int\",\n    \"bytes_little_endian_to_int\",\n    \"clear_bit\",\n    \"count_flips_to_convert\",\n    \"count_ones_iter\",\n    \"count_ones_recur\",\n    \"find_difference\",\n    \"find_missing_number\",\n    \"find_missing_number2\",\n    \"flip_bit_longest_seq\",\n    \"get_bit\",\n    \"has_alternative_bit\",\n    \"has_alternative_bit_fast\",\n    \"insert_mult_bits\",\n    \"insert_one_bit\",\n    \"int_to_bytes_big_endian\",\n    \"int_to_bytes_little_endian\",\n    \"is_power_of_two\",\n    \"remove_bit\",\n    \"reverse_bits\",\n    \"set_bit\",\n    \"single_number\",\n    \"single_number2\",\n    \"single_number3\",\n    \"subsets\",\n    \"swap_pair\",\n    \"update_bit\",\n    \"gray_code\",\n    \"gray_to_binary\",\n]\n"
  },
  {
    "path": "algorithms/bit_manipulation/add_bitwise_operator.py",
    "content": "\"\"\"\nAdd Bitwise Operator\n\nAdd two positive integers without using the '+' operator, using only\nbitwise operations (AND, XOR, shift).\n\nReference: https://en.wikipedia.org/wiki/Adder_(electronics)\n\nComplexity:\n    Time:  O(log n) where n is the larger of the two inputs\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef add_bitwise_operator(first: int, second: int) -> int:\n    \"\"\"Add two non-negative integers using only bitwise operations.\n\n    Args:\n        first: First non-negative integer operand.\n        second: Second non-negative integer operand.\n\n    Returns:\n        The sum of first and second.\n\n    Examples:\n        >>> add_bitwise_operator(2, 3)\n        5\n        >>> add_bitwise_operator(0, 0)\n        0\n    \"\"\"\n    while second:\n        carry = first & second\n        first = first ^ second\n        second = carry << 1\n    return first\n"
  },
  {
    "path": "algorithms/bit_manipulation/binary_gap.py",
    "content": "\"\"\"\nBinary Gap\n\nGiven a positive integer N, find and return the longest distance between two\nconsecutive 1-bits in the binary representation of N. If there are not two\nconsecutive 1-bits, return 0.\n\nReference: https://en.wikipedia.org/wiki/Hamming_distance\n\nComplexity:\n    Time:  O(log n) where n is the input integer\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef binary_gap(number: int) -> int:\n    \"\"\"Find the longest distance between consecutive 1-bits in binary.\n\n    Args:\n        number: A positive integer to examine.\n\n    Returns:\n        The longest gap between consecutive 1-bits, or 0 if fewer\n        than two 1-bits exist.\n\n    Examples:\n        >>> binary_gap(22)\n        2\n        >>> binary_gap(8)\n        0\n    \"\"\"\n    last_one_position = None\n    longest_gap = 0\n    index = 0\n    while number != 0:\n        if number & 1:\n            if last_one_position is not None:\n                longest_gap = max(longest_gap, index - last_one_position)\n            last_one_position = index\n        index += 1\n        number >>= 1\n    return longest_gap\n"
  },
  {
    "path": "algorithms/bit_manipulation/bit_operation.py",
    "content": "\"\"\"\nFundamental Bit Operations\n\nBasic bit manipulation operations: get, set, clear, and update individual\nbits at a specific position in an integer.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    Time:  O(1) for all operations\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef get_bit(number: int, position: int) -> int:\n    \"\"\"Get the bit value at a specific position.\n\n    Shifts 1 over by *position* bits and ANDs with *number* to isolate\n    the target bit.\n\n    Args:\n        number: The integer to inspect.\n        position: Zero-based bit index (0 is the least significant bit).\n\n    Returns:\n        1 if the bit at *position* is set, 0 otherwise.\n\n    Examples:\n        >>> get_bit(22, 2)\n        1\n        >>> get_bit(22, 3)\n        0\n    \"\"\"\n    return (number & (1 << position)) != 0\n\n\ndef set_bit(number: int, position: int) -> int:\n    \"\"\"Set the bit at a specific position to 1.\n\n    Shifts 1 over by *position* bits and ORs with *number* so that only\n    the bit at *position* is turned on.\n\n    Args:\n        number: The integer to modify.\n        position: Zero-based bit index to set.\n\n    Returns:\n        The integer with the bit at *position* set to 1.\n\n    Examples:\n        >>> set_bit(22, 3)\n        30\n    \"\"\"\n    return number | (1 << position)\n\n\ndef clear_bit(number: int, position: int) -> int:\n    \"\"\"Clear the bit at a specific position to 0.\n\n    Creates a mask with all bits set except at *position*, then ANDs\n    with *number*.\n\n    Args:\n        number: The integer to modify.\n        position: Zero-based bit index to clear.\n\n    Returns:\n        The integer with the bit at *position* cleared to 0.\n\n    Examples:\n        >>> clear_bit(22, 2)\n        18\n    \"\"\"\n    mask = ~(1 << position)\n    return number & mask\n\n\ndef update_bit(number: int, position: int, bit: int) -> int:\n    \"\"\"Update the bit at a specific position to a given value.\n\n    First clears the bit at *position*, then ORs in the new *bit* value\n    shifted to that position.\n\n    Args:\n        number: The integer to modify.\n        position: Zero-based bit index to update.\n        bit: The new bit value (0 or 1).\n\n    Returns:\n        The integer with the bit at *position* set to *bit*.\n\n    Examples:\n        >>> update_bit(22, 3, 1)\n        30\n        >>> update_bit(22, 2, 0)\n        18\n    \"\"\"\n    mask = ~(1 << position)\n    return (number & mask) | (bit << position)\n"
  },
  {
    "path": "algorithms/bit_manipulation/bytes_int_conversion.py",
    "content": "\"\"\"\nBytes-Integer Conversion\n\nConvert between Python integers and raw byte sequences in both big-endian\nand little-endian byte orders.\n\nReference: https://en.wikipedia.org/wiki/Endianness\n\nComplexity:\n    Time:  O(b) where b is the number of bytes in the representation\n    Space: O(b)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef int_to_bytes_big_endian(number: int) -> bytes:\n    \"\"\"Convert a non-negative integer to bytes in big-endian order.\n\n    Args:\n        number: A non-negative integer to convert.\n\n    Returns:\n        A bytes object with the most significant byte first.\n\n    Examples:\n        >>> int_to_bytes_big_endian(17)\n        b'\\\\x11'\n    \"\"\"\n    byte_buffer: deque[int] = deque()\n    while number > 0:\n        byte_buffer.appendleft(number & 0xFF)\n        number >>= 8\n    return bytes(byte_buffer)\n\n\ndef int_to_bytes_little_endian(number: int) -> bytes:\n    \"\"\"Convert a non-negative integer to bytes in little-endian order.\n\n    Args:\n        number: A non-negative integer to convert.\n\n    Returns:\n        A bytes object with the least significant byte first.\n\n    Examples:\n        >>> int_to_bytes_little_endian(17)\n        b'\\\\x11'\n    \"\"\"\n    byte_buffer: list[int] = []\n    while number > 0:\n        byte_buffer.append(number & 0xFF)\n        number >>= 8\n    return bytes(byte_buffer)\n\n\ndef bytes_big_endian_to_int(byte_string: bytes) -> int:\n    \"\"\"Convert a big-endian byte sequence to an integer.\n\n    Args:\n        byte_string: Bytes with the most significant byte first.\n\n    Returns:\n        The decoded integer value.\n\n    Examples:\n        >>> bytes_big_endian_to_int(b'\\\\x11')\n        17\n    \"\"\"\n    number = 0\n    for byte in byte_string:\n        number <<= 8\n        number += byte\n    return number\n\n\ndef bytes_little_endian_to_int(byte_string: bytes) -> int:\n    \"\"\"Convert a little-endian byte sequence to an integer.\n\n    Args:\n        byte_string: Bytes with the least significant byte first.\n\n    Returns:\n        The decoded integer value.\n\n    Examples:\n        >>> bytes_little_endian_to_int(b'\\\\x11')\n        17\n    \"\"\"\n    number = 0\n    exponent = 0\n    for byte in byte_string:\n        number += byte << exponent\n        exponent += 8\n    return number\n"
  },
  {
    "path": "algorithms/bit_manipulation/count_flips_to_convert.py",
    "content": "\"\"\"\nCount Flips to Convert\n\nDetermine the minimal number of bits you would need to flip to convert\ninteger A to integer B. Uses XOR to find differing bits and Brian\nKernighan's algorithm to count them.\n\nReference: https://en.wikipedia.org/wiki/Hamming_distance\n\nComplexity:\n    Time:  O(k) where k is the number of differing bits\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count_flips_to_convert(first: int, second: int) -> int:\n    \"\"\"Count the number of bit flips needed to convert one integer to another.\n\n    Args:\n        first: The source integer.\n        second: The target integer.\n\n    Returns:\n        The number of bits that differ between *first* and *second*.\n\n    Examples:\n        >>> count_flips_to_convert(29, 15)\n        2\n        >>> count_flips_to_convert(34, 34)\n        0\n    \"\"\"\n    diff = first ^ second\n    count = 0\n    while diff:\n        diff &= diff - 1\n        count += 1\n    return count\n"
  },
  {
    "path": "algorithms/bit_manipulation/count_ones.py",
    "content": "\"\"\"\nCount Ones (Hamming Weight)\n\nCount the number of 1-bits in the binary representation of an unsigned\ninteger using Brian Kernighan's algorithm.\n\nReference: https://en.wikipedia.org/wiki/Hamming_weight\n\nComplexity:\n    Time:  O(k) where k is the number of set bits\n    Space: O(1) iterative / O(k) recursive (call stack)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count_ones_recur(number: int) -> int:\n    \"\"\"Count set bits using Brian Kernighan's algorithm (recursive).\n\n    Args:\n        number: A non-negative integer.\n\n    Returns:\n        The number of 1-bits in the binary representation.\n\n    Examples:\n        >>> count_ones_recur(8)\n        1\n        >>> count_ones_recur(63)\n        6\n    \"\"\"\n    if not number:\n        return 0\n    return 1 + count_ones_recur(number & (number - 1))\n\n\ndef count_ones_iter(number: int) -> int:\n    \"\"\"Count set bits using Brian Kernighan's algorithm (iterative).\n\n    Args:\n        number: A non-negative integer.\n\n    Returns:\n        The number of 1-bits in the binary representation.\n\n    Examples:\n        >>> count_ones_iter(8)\n        1\n        >>> count_ones_iter(63)\n        6\n    \"\"\"\n    count = 0\n    while number:\n        number &= number - 1\n        count += 1\n    return count\n"
  },
  {
    "path": "algorithms/bit_manipulation/find_difference.py",
    "content": "\"\"\"\nFind the Difference\n\nGiven two strings where the second is generated by shuffling the first and\nadding one extra letter, find the added letter using XOR.\n\nReference: https://en.wikipedia.org/wiki/Exclusive_or\n\nComplexity:\n    Time:  O(n) where n is the length of the longer string\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_difference(original: str, shuffled: str) -> str:\n    \"\"\"Find the single character added to a shuffled copy of a string.\n\n    Uses XOR on all character code points; paired characters cancel out,\n    leaving only the extra character.\n\n    Args:\n        original: The original string.\n        shuffled: The shuffled string with one extra character.\n\n    Returns:\n        The single character that was added.\n\n    Examples:\n        >>> find_difference(\"abcd\", \"abecd\")\n        'e'\n    \"\"\"\n    xor_result = 0\n    for character in original + shuffled:\n        xor_result ^= ord(character)\n    return chr(xor_result)\n"
  },
  {
    "path": "algorithms/bit_manipulation/find_missing_number.py",
    "content": "\"\"\"\nFind Missing Number\n\nGiven a sequence of unique integers in the range [0..n] with one value\nmissing, find and return that missing number. Two approaches are provided:\nXOR-based and summation-based.\n\nReference: https://en.wikipedia.org/wiki/Exclusive_or\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_missing_number(nums: list[int]) -> int:\n    \"\"\"Find the missing number using XOR.\n\n    XORs every element with its expected index so that all paired values\n    cancel out, leaving only the missing number.\n\n    Args:\n        nums: A list of unique integers from 0..n with one missing.\n\n    Returns:\n        The missing integer.\n\n    Examples:\n        >>> find_missing_number([4, 1, 3, 0, 6, 5, 2])\n        7\n        >>> find_missing_number([0])\n        1\n    \"\"\"\n    missing = 0\n    for index, number in enumerate(nums):\n        missing ^= number\n        missing ^= index + 1\n    return missing\n\n\ndef find_missing_number2(nums: list[int]) -> int:\n    \"\"\"Find the missing number using arithmetic summation.\n\n    Computes the expected sum of 0..n and subtracts the actual sum of\n    the list to isolate the missing value.\n\n    Args:\n        nums: A list of unique integers from 0..n with one missing.\n\n    Returns:\n        The missing integer.\n\n    Examples:\n        >>> find_missing_number2([4, 1, 3, 0, 6, 5, 2])\n        7\n        >>> find_missing_number2([0])\n        1\n    \"\"\"\n    total = sum(nums)\n    length = len(nums)\n    expected_total = length * (length + 1) // 2\n    return expected_total - total\n"
  },
  {
    "path": "algorithms/bit_manipulation/flip_bit_longest_sequence.py",
    "content": "\"\"\"\nFlip Bit Longest Sequence\n\nGiven an integer, find the length of the longest sequence of 1-bits you\ncan create by flipping exactly one 0-bit to a 1-bit.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    Time:  O(b) where b is the number of bits in the integer\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef flip_bit_longest_seq(number: int) -> int:\n    \"\"\"Find the longest 1-bit run achievable by flipping a single 0-bit.\n\n    Tracks the current run length and the previous run length to\n    determine the best sequence that can be formed by bridging two\n    runs with a single flipped bit.\n\n    Args:\n        number: A non-negative integer.\n\n    Returns:\n        The length of the longest sequence of 1-bits after one flip.\n\n    Examples:\n        >>> flip_bit_longest_seq(1775)\n        8\n        >>> flip_bit_longest_seq(0)\n        1\n    \"\"\"\n    current_length = 0\n    previous_length = 0\n    max_length = 0\n\n    while number:\n        if number & 1 == 1:\n            current_length += 1\n        elif number & 1 == 0:\n            previous_length = 0 if number & 2 == 0 else current_length\n            current_length = 0\n\n        max_length = max(max_length, previous_length + current_length)\n        number >>= 1\n\n    return max_length + 1\n"
  },
  {
    "path": "algorithms/bit_manipulation/gray_code.py",
    "content": "\"\"\"Gray code — generate n-bit Gray code sequences.\n\nA Gray code is an ordering of binary numbers such that successive values\ndiffer in exactly one bit. Used in error correction and rotary encoders.\n\nInspired by PR #932 (Simranstha045).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef gray_code(n: int) -> list[int]:\n    \"\"\"Return the n-bit Gray code sequence as a list of integers.\n\n    Uses the reflection (mirror) construction:\n        gray(i) = i ^ (i >> 1)\n\n    >>> gray_code(2)\n    [0, 1, 3, 2]\n    >>> gray_code(3)\n    [0, 1, 3, 2, 6, 7, 5, 4]\n    \"\"\"\n    return [i ^ (i >> 1) for i in range(1 << n)]\n\n\ndef gray_to_binary(gray: int) -> int:\n    \"\"\"Convert a Gray-coded integer back to standard binary.\"\"\"\n    mask = gray >> 1\n    while mask:\n        gray ^= mask\n        mask >>= 1\n    return gray\n"
  },
  {
    "path": "algorithms/bit_manipulation/has_alternative_bit.py",
    "content": "\"\"\"\nHas Alternating Bits\n\nCheck whether a positive integer has alternating bits, meaning no two\nadjacent bits share the same value.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    has_alternative_bit:      O(number of bits)\n    has_alternative_bit_fast: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef has_alternative_bit(number: int) -> bool:\n    \"\"\"Check for alternating bits by scanning each pair of adjacent bits.\n\n    Args:\n        number: A positive integer to check.\n\n    Returns:\n        True if every pair of adjacent bits differs, False otherwise.\n\n    Examples:\n        >>> has_alternative_bit(5)\n        True\n        >>> has_alternative_bit(7)\n        False\n    \"\"\"\n    first_bit = 0\n    second_bit = 0\n    while number:\n        first_bit = number & 1\n        if number >> 1:\n            second_bit = (number >> 1) & 1\n            if not first_bit ^ second_bit:\n                return False\n        else:\n            return True\n        number >>= 1\n    return True\n\n\ndef has_alternative_bit_fast(number: int) -> bool:\n    \"\"\"Check for alternating bits using O(1) bitmask arithmetic.\n\n    Args:\n        number: A positive integer to check.\n\n    Returns:\n        True if every pair of adjacent bits differs, False otherwise.\n\n    Examples:\n        >>> has_alternative_bit_fast(5)\n        True\n        >>> has_alternative_bit_fast(7)\n        False\n    \"\"\"\n    mask_even_bits = int(\"aaaaaaaa\", 16)  # ...10101010\n    mask_odd_bits = int(\"55555555\", 16)  # ...01010101\n    return mask_even_bits == (number + (number ^ mask_even_bits)) or mask_odd_bits == (\n        number + (number ^ mask_odd_bits)\n    )\n"
  },
  {
    "path": "algorithms/bit_manipulation/insert_bit.py",
    "content": "\"\"\"\nInsert Bit\n\nInsert one or more bits into an integer at a specific bit position.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef insert_one_bit(number: int, bit: int, position: int) -> int:\n    \"\"\"Insert a single bit at a specific position in an integer.\n\n    Splits the number at *position*, shifts the upper part left by one\n    to make room, inserts *bit*, and merges with the lower part.\n\n    Args:\n        number: The integer to modify.\n        bit: The bit value to insert (0 or 1).\n        position: Zero-based index at which to insert the bit.\n\n    Returns:\n        The resulting integer after insertion.\n\n    Examples:\n        >>> insert_one_bit(21, 1, 2)\n        45\n        >>> insert_one_bit(21, 0, 2)\n        41\n    \"\"\"\n    upper = number >> position\n    upper = (upper << 1) | bit\n    upper = upper << position\n    lower = ((1 << position) - 1) & number\n    return lower | upper\n\n\ndef insert_mult_bits(number: int, bits: int, length: int, position: int) -> int:\n    \"\"\"Insert multiple bits at a specific position in an integer.\n\n    Splits the number at *position*, shifts the upper part left by\n    *length* positions, inserts the *bits* value, and merges with the\n    lower part.\n\n    Args:\n        number: The integer to modify.\n        bits: The bit pattern to insert.\n        length: The number of bits in the pattern.\n        position: Zero-based index at which to insert.\n\n    Returns:\n        The resulting integer after insertion.\n\n    Examples:\n        >>> insert_mult_bits(5, 7, 3, 1)\n        47\n        >>> insert_mult_bits(5, 7, 3, 3)\n        61\n    \"\"\"\n    upper = number >> position\n    upper = (upper << length) | bits\n    upper = upper << position\n    lower = ((1 << position) - 1) & number\n    return lower | upper\n"
  },
  {
    "path": "algorithms/bit_manipulation/power_of_two.py",
    "content": "\"\"\"\nPower of Two\n\nDetermine whether a given integer is a power of two using bit manipulation.\nA power of two has exactly one set bit, so ``n & (n - 1)`` clears that bit\nand yields zero.\n\nReference: https://en.wikipedia.org/wiki/Power_of_two\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_power_of_two(number: int) -> bool:\n    \"\"\"Check whether an integer is a power of two.\n\n    Args:\n        number: The integer to test.\n\n    Returns:\n        True if *number* is a positive power of two, False otherwise.\n\n    Examples:\n        >>> is_power_of_two(64)\n        True\n        >>> is_power_of_two(91)\n        False\n        >>> is_power_of_two(0)\n        False\n    \"\"\"\n    return number > 0 and not number & (number - 1)\n"
  },
  {
    "path": "algorithms/bit_manipulation/remove_bit.py",
    "content": "\"\"\"\nRemove Bit\n\nRemove a single bit at a specific position from an integer, shifting\nhigher bits down to fill the gap.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef remove_bit(number: int, position: int) -> int:\n    \"\"\"Remove the bit at a specific position from an integer.\n\n    Splits the number around *position*, shifts the upper part right\n    by one to collapse the gap, and merges with the lower part.\n\n    Args:\n        number: The integer to modify.\n        position: Zero-based index of the bit to remove.\n\n    Returns:\n        The resulting integer after removal.\n\n    Examples:\n        >>> remove_bit(21, 2)\n        9\n        >>> remove_bit(21, 4)\n        5\n        >>> remove_bit(21, 0)\n        10\n    \"\"\"\n    upper = number >> (position + 1)\n    upper = upper << position\n    lower = ((1 << position) - 1) & number\n    return upper | lower\n"
  },
  {
    "path": "algorithms/bit_manipulation/reverse_bits.py",
    "content": "\"\"\"\nReverse Bits\n\nReverse the bits of a 32-bit unsigned integer.\n\nReference: https://en.wikipedia.org/wiki/Bit_reversal\n\nComplexity:\n    Time:  O(1) -- always iterates exactly 32 times\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef reverse_bits(number: int) -> int:\n    \"\"\"Reverse all 32 bits of an unsigned integer.\n\n    Args:\n        number: A 32-bit unsigned integer (0 to 2**32 - 1).\n\n    Returns:\n        The integer formed by reversing the bit order.\n\n    Examples:\n        >>> reverse_bits(43261596)\n        964176192\n        >>> reverse_bits(0)\n        0\n    \"\"\"\n    result = 0\n    for _ in range(32):\n        result = (result << 1) + (number & 1)\n        number >>= 1\n    return result\n"
  },
  {
    "path": "algorithms/bit_manipulation/single_number.py",
    "content": "\"\"\"\nSingle Number\n\nGiven an array of integers where every element appears twice except for\none, find the unique element using XOR.\n\nReference: https://en.wikipedia.org/wiki/Exclusive_or\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef single_number(nums: list[int]) -> int:\n    \"\"\"Find the element that appears only once (all others appear twice).\n\n    XORs all values together; paired values cancel to zero, leaving the\n    unique value.\n\n    Args:\n        nums: A list of integers where every element except one appears\n              an even number of times.\n\n    Returns:\n        The single element, or 0 if all elements are paired.\n\n    Examples:\n        >>> single_number([1, 0, 2, 1, 2, 3, 3])\n        0\n        >>> single_number([101])\n        101\n    \"\"\"\n    result = 0\n    for number in nums:\n        result ^= number\n    return result\n"
  },
  {
    "path": "algorithms/bit_manipulation/single_number2.py",
    "content": "\"\"\"\nSingle Number 2\n\nGiven an array of integers where every element appears three times except\nfor one (which appears exactly once), find that unique element using\nconstant space and linear time.\n\nReference: https://en.wikipedia.org/wiki/Exclusive_or\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef single_number2(nums: list[int]) -> int:\n    \"\"\"Find the element that appears once (all others appear three times).\n\n    Uses two accumulators (*ones* and *twos*) to track bits that have\n    appeared once and twice respectively; bits appearing a third time\n    are cleared from both accumulators.\n\n    Args:\n        nums: A list of integers where every element except one appears\n              exactly three times.\n\n    Returns:\n        The single element that appears only once.\n\n    Examples:\n        >>> single_number2([4, 2, 3, 2, 1, 1, 4, 2, 4, 1])\n        3\n    \"\"\"\n    ones, twos = 0, 0\n    for index in range(len(nums)):\n        ones = (ones ^ nums[index]) & ~twos\n        twos = (twos ^ nums[index]) & ~ones\n    return ones\n"
  },
  {
    "path": "algorithms/bit_manipulation/single_number3.py",
    "content": "\"\"\"\nSingle Number 3\n\nGiven an array where exactly two elements appear once and all others\nappear exactly twice, find those two unique elements in O(n) time and\nO(1) space.\n\nReference: https://en.wikipedia.org/wiki/Exclusive_or\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef single_number3(nums: list[int]) -> list[int]:\n    \"\"\"Find the two elements that each appear exactly once.\n\n    Uses XOR to isolate the combined signature of the two unique values,\n    then partitions all numbers by a distinguishing bit to separate them.\n\n    Args:\n        nums: A list of integers where exactly two elements appear once\n              and all others appear exactly twice.\n\n    Returns:\n        A list containing the two unique elements.\n\n    Examples:\n        >>> sorted(single_number3([1, 2, 1, 3, 2, 5]))\n        [3, 5]\n    \"\"\"\n    xor_both = 0\n    for number in nums:\n        xor_both ^= number\n\n    rightmost_set_bit = xor_both & (-xor_both)\n\n    first, second = 0, 0\n    for number in nums:\n        if number & rightmost_set_bit:\n            first ^= number\n        else:\n            second ^= number\n    return [first, second]\n"
  },
  {
    "path": "algorithms/bit_manipulation/subsets.py",
    "content": "\"\"\"\nSubsets via Bit Manipulation\n\nGenerate all possible subsets of a set of distinct integers using bitmask\nenumeration. Each integer from 0 to 2^n - 1 represents a unique subset.\n\nReference: https://en.wikipedia.org/wiki/Power_set\n\nComplexity:\n    Time:  O(n * 2^n)\n    Space: O(n * 2^n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef subsets(nums: list[int]) -> set[tuple[int, ...]]:\n    \"\"\"Return all subsets of the given list as a set of tuples.\n\n    Uses bitmask enumeration: for each number from 0 to 2^n - 1, the\n    set bits indicate which elements are included in that subset.\n\n    Args:\n        nums: A list of distinct integers.\n\n    Returns:\n        A set of tuples, each representing one subset.\n\n    Examples:\n        >>> sorted(subsets([1, 2, 3]))\n        [(), (1,), (1, 2), (1, 2, 3), (1, 3), (2,), (2, 3), (3,)]\n    \"\"\"\n    length = len(nums)\n    total = 1 << length\n    result: set[tuple[int, ...]] = set()\n\n    for mask in range(total):\n        subset = tuple(\n            number for bit_index, number in enumerate(nums) if mask & 1 << bit_index\n        )\n        result.add(subset)\n\n    return result\n"
  },
  {
    "path": "algorithms/bit_manipulation/swap_pair.py",
    "content": "\"\"\"\nSwap Pair\n\nSwap odd and even bits of an integer using bitmask operations. Bit 0 is\nswapped with bit 1, bit 2 with bit 3, and so on.\n\nReference: https://en.wikipedia.org/wiki/Bit_manipulation\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef swap_pair(number: int) -> int:\n    \"\"\"Swap every pair of adjacent bits in an integer.\n\n    Masks odd-positioned bits (0xAAAAAAAA), shifts them right by one,\n    masks even-positioned bits (0x55555555), shifts them left by one,\n    and merges the results.\n\n    Args:\n        number: A non-negative integer.\n\n    Returns:\n        The integer with each adjacent pair of bits swapped.\n\n    Examples:\n        >>> swap_pair(22)\n        41\n        >>> swap_pair(10)\n        5\n    \"\"\"\n    odd_bits = (number & int(\"AAAAAAAA\", 16)) >> 1\n    even_bits = (number & int(\"55555555\", 16)) << 1\n    return odd_bits | even_bits\n"
  },
  {
    "path": "algorithms/common/__init__.py",
    "content": "\"\"\"Shared data types used across all algorithm categories.\n\nThese types are the connective tissue of the library — every algorithm\naccepts and returns these types, making them composable.\n\n    >>> from algorithms.common import TreeNode, ListNode, Graph\n\"\"\"\n\nfrom algorithms.common.graph import Graph\nfrom algorithms.common.list_node import ListNode\nfrom algorithms.common.tree_node import TreeNode\n\n__all__ = [\"TreeNode\", \"ListNode\", \"Graph\"]\n"
  },
  {
    "path": "algorithms/common/graph.py",
    "content": "\"\"\"Graph type shared across all graph algorithms.\n\nThis module provides the universal Graph used by every graph algorithm\nin this library. Using a single shared type means you can compose\nalgorithms: build a graph, run BFS to check connectivity, then run\nDijkstra for shortest paths — all on the same object.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass, field\n\n\n@dataclass\nclass Graph:\n    \"\"\"A weighted directed graph using adjacency dict representation.\n\n    Supports weighted/unweighted and directed/undirected graphs. The\n    graph is stored as ``{node: {neighbor: weight}}``. For unweighted\n    graphs, use the :meth:`unweighted` factory or set weights to 1.\n\n    Attributes:\n        adj: Adjacency dict mapping each node to ``{neighbor: weight}``.\n        directed: Whether this is a directed graph.\n\n    Examples:\n        >>> g = Graph({\"a\": {\"b\": 1, \"c\": 4}, \"b\": {\"c\": 2}})\n        >>> g.adj[\"a\"]\n        {'b': 1, 'c': 4}\n\n        >>> g = Graph.unweighted({\"a\": [\"b\", \"c\"], \"b\": [\"c\"]})\n        >>> g.adj[\"a\"]\n        {'b': 1, 'c': 1}\n    \"\"\"\n\n    adj: dict[str, dict[str, float]] = field(default_factory=dict)\n    directed: bool = True\n\n    @classmethod\n    def unweighted(cls, adj: dict[str, list[str]], directed: bool = True) -> Graph:\n        \"\"\"Create a graph from an unweighted adjacency list.\n\n        Args:\n            adj: Mapping of node to list of neighbors.\n            directed: Whether edges are one-directional.\n\n        Returns:\n            A Graph with all edge weights set to 1.\n\n        Examples:\n            >>> g = Graph.unweighted({\"a\": [\"b\", \"c\"], \"b\": [\"c\"]})\n            >>> g.adj[\"b\"]\n            {'c': 1}\n        \"\"\"\n        weighted = {\n            node: {neighbor: 1 for neighbor in neighbors}\n            for node, neighbors in adj.items()\n        }\n        return cls(adj=weighted, directed=directed)\n\n    def nodes(self) -> set[str]:\n        \"\"\"Return all nodes in the graph.\n\n        Returns:\n            Set of all node identifiers, including nodes that only\n            appear as neighbors.\n\n        Examples:\n            >>> g = Graph({\"a\": {\"b\": 1}})\n            >>> sorted(g.nodes())\n            ['a', 'b']\n        \"\"\"\n        all_nodes: set[str] = set(self.adj.keys())\n        for neighbors in self.adj.values():\n            all_nodes.update(neighbors.keys())\n        return all_nodes\n\n    def add_edge(self, source: str, target: str, weight: float = 1) -> None:\n        \"\"\"Add an edge to the graph.\n\n        For undirected graphs, the reverse edge is also added.\n\n        Args:\n            source: Source node.\n            target: Target node.\n            weight: Edge weight (default 1).\n\n        Examples:\n            >>> g = Graph()\n            >>> g.add_edge(\"a\", \"b\", 5)\n            >>> g.adj[\"a\"][\"b\"]\n            5\n        \"\"\"\n        if source not in self.adj:\n            self.adj[source] = {}\n        self.adj[source][target] = weight\n        if not self.directed:\n            if target not in self.adj:\n                self.adj[target] = {}\n            self.adj[target][source] = weight\n"
  },
  {
    "path": "algorithms/common/list_node.py",
    "content": "\"\"\"Singly linked list node shared across all linked list algorithms.\n\nThis module provides the universal ListNode used by every linked list\nalgorithm in this library. Using a single shared type means you can\ncompose algorithms: merge two lists, reverse the result, check if\nit's a palindrome.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\n\n\n@dataclass\nclass ListNode:\n    \"\"\"A node in a singly linked list.\n\n    Attributes:\n        val: The value stored in this node.\n        next: Reference to the next node.\n\n    Examples:\n        >>> head = ListNode(1, ListNode(2, ListNode(3)))\n        >>> head.val\n        1\n        >>> head.next.val\n        2\n    \"\"\"\n\n    val: int = 0\n    next: ListNode | None = None\n"
  },
  {
    "path": "algorithms/common/tree_node.py",
    "content": "\"\"\"Binary tree node shared across all tree algorithms.\n\nThis module provides the universal TreeNode used by every tree algorithm\nin this library. Using a single shared type means you can compose\nalgorithms freely: build a BST, invert it, then traverse it.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\n\n\n@dataclass\nclass TreeNode:\n    \"\"\"A node in a binary tree.\n\n    Attributes:\n        val: The value stored in this node.\n        left: Reference to the left child.\n        right: Reference to the right child.\n\n    Examples:\n        >>> root = TreeNode(1, TreeNode(2), TreeNode(3))\n        >>> root.left.val\n        2\n        >>> root.right.val\n        3\n    \"\"\"\n\n    val: int = 0\n    left: TreeNode | None = None\n    right: TreeNode | None = None\n"
  },
  {
    "path": "algorithms/compression/__init__.py",
    "content": "from .elias import elias_delta, elias_gamma\nfrom .huffman_coding import HuffmanCoding\nfrom .rle_compression import decode_rle, encode_rle\n\n__all__ = [\n    \"HuffmanCoding\",\n    \"decode_rle\",\n    \"elias_delta\",\n    \"elias_gamma\",\n    \"encode_rle\",\n]\n"
  },
  {
    "path": "algorithms/compression/elias.py",
    "content": "\"\"\"\nElias Gamma and Delta Coding\n\nUniversal codes for encoding positive integers. Elias gamma code uses a unary\nprefix followed by a binary suffix. Elias delta code nests gamma coding for\nthe length prefix. Both were developed by Peter Elias.\n\nReference: https://en.wikipedia.org/wiki/Elias_gamma_coding\n\nComplexity:\n    Time:  O(log n) per encoded integer\n    Space: O(log n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom math import log\n\n\ndef _log2(x: int | float) -> float:\n    \"\"\"Compute log base 2.\n\n    Args:\n        x: A positive number.\n\n    Returns:\n        The base-2 logarithm of x.\n    \"\"\"\n    return log(x, 2)\n\n\ndef _binary(x: int, length: int = 1) -> str:\n    \"\"\"Return the binary representation of x zero-padded to length digits.\n\n    Args:\n        x: A non-negative integer.\n        length: Minimum number of binary digits.\n\n    Returns:\n        A binary string.\n    \"\"\"\n    fmt = f\"{{0:0{length}b}}\"\n    return fmt.format(x)\n\n\ndef _unary(x: int) -> str:\n    \"\"\"Return the unary representation of x.\n\n    Args:\n        x: A positive integer.\n\n    Returns:\n        A unary-coded string (x-1 ones followed by a zero).\n    \"\"\"\n    return (x - 1) * \"1\" + \"0\"\n\n\ndef _elias_generic(\n    length_encoding: callable,\n    x: int,\n) -> str:\n    \"\"\"Generic Elias encoding using a pluggable length-encoding function.\n\n    Args:\n        length_encoding: A function to encode the length prefix.\n        x: A non-negative integer to encode.\n\n    Returns:\n        The Elias-coded bit string.\n    \"\"\"\n    if x == 0:\n        return \"0\"\n\n    first_part = 1 + int(_log2(x))\n    remainder = x - 2 ** int(_log2(x))\n    bit_count = int(_log2(x))\n\n    return length_encoding(first_part) + _binary(remainder, bit_count)\n\n\ndef elias_gamma(x: int) -> str:\n    \"\"\"Encode a positive integer using Elias gamma coding.\n\n    Args:\n        x: A non-negative integer.\n\n    Returns:\n        The Elias gamma coded bit string.\n\n    Examples:\n        >>> elias_gamma(1)\n        '0'\n        >>> elias_gamma(5)\n        '00101'\n    \"\"\"\n    return _elias_generic(_unary, x)\n\n\ndef elias_delta(x: int) -> str:\n    \"\"\"Encode a positive integer using Elias delta coding.\n\n    Args:\n        x: A non-negative integer.\n\n    Returns:\n        The Elias delta coded bit string.\n\n    Examples:\n        >>> elias_delta(1)\n        '0'\n        >>> elias_delta(5)\n        '01101'\n    \"\"\"\n    return _elias_generic(elias_gamma, x)\n"
  },
  {
    "path": "algorithms/compression/huffman_coding.py",
    "content": "\"\"\"\nHuffman Coding\n\nAn efficient method of lossless data compression. Symbols appearing more\nfrequently are encoded with shorter bit strings while less frequent symbols\nreceive longer codes.\n\nReference: https://en.wikipedia.org/wiki/Huffman_coding\n\nComplexity:\n    Time:  O(n log n) for encoding (heap operations)\n    Space: O(n) for the code table\n\"\"\"\n\nfrom __future__ import annotations\n\nimport heapq\nfrom collections import defaultdict, deque\n\n\nclass Node:\n    \"\"\"A node in the Huffman tree.\"\"\"\n\n    def __init__(\n        self,\n        frequency: int = 0,\n        sign: int | None = None,\n        left: Node | None = None,\n        right: Node | None = None,\n    ) -> None:\n        self.frequency = frequency\n        self.sign = sign\n        self.left = left\n        self.right = right\n\n    def __lt__(self, other: Node) -> bool:\n        return self.frequency < other.frequency\n\n    def __gt__(self, other: Node) -> bool:\n        return self.frequency > other.frequency\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Node):\n            return NotImplemented\n        return self.frequency == other.frequency\n\n    def __str__(self) -> str:\n        return f\"<ch: {self.sign}: {self.frequency}>\"\n\n    def __repr__(self) -> str:\n        return f\"<ch: {self.sign}: {self.frequency}>\"\n\n\nclass HuffmanReader:\n    \"\"\"Reads Huffman-encoded binary data from a file.\"\"\"\n\n    def __init__(self, file: object) -> None:\n        self.file = file\n        self.buffer: list[str] = []\n        self.is_last_byte: bool = False\n\n    def get_number_of_additional_bits_in_the_last_byte(self) -> int:\n        \"\"\"Read the 3-bit header indicating extra padding bits.\n\n        Returns:\n            The number of additional padding bits in the last byte.\n        \"\"\"\n        bin_num = self.get_bit() + self.get_bit() + self.get_bit()\n        return int(bin_num, 2)\n\n    def load_tree(self) -> Node:\n        \"\"\"Reconstruct the Huffman tree from the file header.\n\n        Returns:\n            The root node of the reconstructed tree.\n        \"\"\"\n        node_stack: deque[Node] = deque()\n        queue_leaves: deque[Node] = deque()\n        root = Node()\n\n        current_node = root\n        is_end_of_tree = False\n        while not is_end_of_tree:\n            current_bit = self.get_bit()\n            if current_bit == \"0\":\n                current_node.left = Node()\n                current_node.right = Node()\n                node_stack.append(current_node.right)\n                current_node = current_node.left\n            else:\n                queue_leaves.append(current_node)\n                if node_stack:\n                    current_node = node_stack.pop()\n                else:\n                    is_end_of_tree = True\n\n        self._fill_tree(queue_leaves)\n        return root\n\n    def _fill_tree(self, leaves_queue: deque[Node]) -> None:\n        \"\"\"Load character values into leaf nodes.\n\n        Args:\n            leaves_queue: Queue of leaf nodes to populate.\n        \"\"\"\n        leaves_queue.reverse()\n        while leaves_queue:\n            node = leaves_queue.pop()\n            char_value = int(self.get_byte(), 2)\n            node.sign = char_value\n\n    def _load_byte(self, buff_limit: int = 8) -> bool:\n        \"\"\"Load the next byte into the buffer if needed.\n\n        Args:\n            buff_limit: Minimum buffer size before loading.\n\n        Returns:\n            True if enough bits are available for reading.\n        \"\"\"\n        if len(self.buffer) <= buff_limit:\n            byte = self.file.read(1)\n            if not byte:\n                return False\n            integer = int.from_bytes(byte, \"big\")\n            self.buffer.extend(list(f\"{integer:08b}\"))\n        return True\n\n    def get_bit(self, buff_limit: int = 8) -> str | int:\n        \"\"\"Read a single bit from the buffer.\n\n        Args:\n            buff_limit: Minimum buffer size before loading.\n\n        Returns:\n            A '0' or '1' character, or -1 if at end of file.\n        \"\"\"\n        if self._load_byte(buff_limit):\n            return self.buffer.pop(0)\n        return -1\n\n    def get_byte(self) -> str | int:\n        \"\"\"Read eight bits from the buffer.\n\n        Returns:\n            An 8-character binary string, or -1 if at end of file.\n        \"\"\"\n        if self._load_byte():\n            byte_list = self.buffer[:8]\n            self.buffer = self.buffer[8:]\n            return \"\".join(byte_list)\n        return -1\n\n\nclass HuffmanWriter:\n    \"\"\"Writes Huffman-encoded binary data to a file.\"\"\"\n\n    def __init__(self, file: object) -> None:\n        self.file = file\n        self.buffer: str = \"\"\n        self.saved_bits: int = 0\n\n    def write_char(self, char: str) -> None:\n        \"\"\"Write a character as its 8-bit ordinal value.\n\n        Args:\n            char: A single character to write.\n        \"\"\"\n        self.write_int(ord(char))\n\n    def write_int(self, num: int) -> None:\n        \"\"\"Write an integer as an 8-bit binary value.\n\n        Args:\n            num: An integer (0-255) to write.\n        \"\"\"\n        bin_int = f\"{num:08b}\"\n        self.write_bits(bin_int)\n\n    def write_bits(self, bits: str) -> None:\n        \"\"\"Write a string of bits, flushing complete bytes.\n\n        Args:\n            bits: A string of '0' and '1' characters.\n        \"\"\"\n        self.saved_bits += len(bits)\n        self.buffer += bits\n        while len(self.buffer) >= 8:\n            integer = int(self.buffer[:8], 2)\n            self.file.write(bytes([integer]))\n            self.buffer = self.buffer[8:]\n\n    def save_tree(self, tree: Node) -> None:\n        \"\"\"Serialize the Huffman tree structure to the file.\n\n        Args:\n            tree: The root node of the Huffman tree.\n        \"\"\"\n        signs: list[int] = []\n        tree_code = \"\"\n\n        def _get_code_tree(node: Node) -> None:\n            nonlocal tree_code\n            if node.sign is not None:\n                signs.append(node.sign)\n            if node.left:\n                tree_code += \"0\"\n                _get_code_tree(node.left)\n            if node.right:\n                tree_code += \"1\"\n                _get_code_tree(node.right)\n\n        _get_code_tree(tree)\n        self.write_bits(tree_code + \"1\")\n        for int_sign in signs:\n            self.write_int(int_sign)\n\n    def _save_information_about_additional_bits(self, additional_bits: int) -> None:\n        \"\"\"Overwrite the first three bits to record padding count.\n\n        Args:\n            additional_bits: The number of padding bits appended.\n        \"\"\"\n        self.file.seek(0)\n        first_byte_raw = self.file.read(1)\n        self.file.seek(0)\n        first_byte = \"{:08b}\".format(int.from_bytes(first_byte_raw, \"big\"))\n        first_byte = f\"{additional_bits:03b}\" + first_byte[3:]\n        self.write_bits(first_byte)\n\n    def close(self) -> None:\n        \"\"\"Flush remaining bits with padding and finalize the file.\"\"\"\n        additional_bits = 8 - len(self.buffer)\n        if additional_bits != 8:\n            self.write_bits(\"0\" * additional_bits)\n            self._save_information_about_additional_bits(additional_bits)\n\n\nclass TreeFinder:\n    \"\"\"Traverses a Huffman tree to decode individual symbols.\"\"\"\n\n    def __init__(self, tree: Node) -> None:\n        self.root = tree\n        self.current_node = tree\n        self.found: int | str | None = None\n\n    def find(self, bit: str) -> bool:\n        \"\"\"Advance one step in the tree and check for a decoded symbol.\n\n        Args:\n            bit: '0' for left, '1' for right.\n\n        Returns:\n            True if a symbol was found at the current node.\n        \"\"\"\n        if bit == \"0\":\n            self.current_node = self.current_node.left\n        elif bit == \"1\":\n            self.current_node = self.current_node.right\n        else:\n            self._reset()\n            return True\n\n        if self.current_node.sign is not None:\n            self._reset(self.current_node.sign)\n            return True\n        return False\n\n    def _reset(self, found: int | str = \"\") -> None:\n        \"\"\"Reset traversal to the root after finding a symbol.\n\n        Args:\n            found: The decoded symbol value.\n        \"\"\"\n        self.found = found\n        self.current_node = self.root\n\n\nclass HuffmanCoding:\n    \"\"\"Provides static methods for Huffman file encoding and decoding.\"\"\"\n\n    def __init__(self) -> None:\n        pass\n\n    @staticmethod\n    def decode_file(file_in_name: str, file_out_name: str) -> None:\n        \"\"\"Decode a Huffman-encoded file.\n\n        Args:\n            file_in_name: Path to the encoded input file.\n            file_out_name: Path to the decoded output file.\n        \"\"\"\n        with open(file_in_name, \"rb\") as file_in, open(file_out_name, \"wb\") as file_out:\n            reader = HuffmanReader(file_in)\n            additional_bits = reader.get_number_of_additional_bits_in_the_last_byte()\n            tree = reader.load_tree()\n            HuffmanCoding._decode_and_write_signs_to_file(\n                file_out, reader, tree, additional_bits\n            )\n\n    @staticmethod\n    def _decode_and_write_signs_to_file(\n        file: object,\n        reader: HuffmanReader,\n        tree: Node,\n        additional_bits: int,\n    ) -> None:\n        \"\"\"Decode bits from reader and write decoded bytes to file.\n\n        Args:\n            file: The output file object.\n            reader: The HuffmanReader providing encoded bits.\n            tree: The root of the Huffman tree.\n            additional_bits: Number of padding bits in the last byte.\n        \"\"\"\n        tree_finder = TreeFinder(tree)\n        is_end_of_file = False\n\n        while not is_end_of_file:\n            bit = reader.get_bit()\n            if bit != -1:\n                while not tree_finder.find(bit):\n                    bit = reader.get_bit(0)\n                file.write(bytes([tree_finder.found]))\n            else:\n                is_end_of_file = True\n                last_byte = reader.buffer\n                last_byte = last_byte[:-additional_bits]\n                for bit in last_byte:\n                    if tree_finder.find(bit):\n                        file.write(bytes([tree_finder.found]))\n\n    @staticmethod\n    def encode_file(file_in_name: str, file_out_name: str) -> None:\n        \"\"\"Encode a file using Huffman coding.\n\n        Args:\n            file_in_name: Path to the raw input file.\n            file_out_name: Path to the encoded output file.\n        \"\"\"\n        with (\n            open(file_in_name, \"rb\") as file_in,\n            open(file_out_name, mode=\"wb+\") as file_out,\n        ):\n            signs_frequency = HuffmanCoding._get_char_frequency(file_in)\n            file_in.seek(0)\n            tree = HuffmanCoding._create_tree(signs_frequency)\n            codes = HuffmanCoding._generate_codes(tree)\n\n            writer = HuffmanWriter(file_out)\n            writer.write_bits(\"000\")\n            writer.save_tree(tree)\n            HuffmanCoding._encode_and_write_signs_to_file(file_in, writer, codes)\n            writer.close()\n\n    @staticmethod\n    def _encode_and_write_signs_to_file(\n        file: object, writer: HuffmanWriter, codes: dict[int, str]\n    ) -> None:\n        \"\"\"Read bytes from file and write their Huffman codes.\n\n        Args:\n            file: The input file object.\n            writer: The HuffmanWriter for output.\n            codes: Mapping of byte values to Huffman code strings.\n        \"\"\"\n        sign = file.read(1)\n        while sign:\n            int_char = int.from_bytes(sign, \"big\")\n            writer.write_bits(codes[int_char])\n            sign = file.read(1)\n\n    @staticmethod\n    def _get_char_frequency(file: object) -> dict[int, int]:\n        \"\"\"Count byte frequencies in a file.\n\n        Args:\n            file: The input file object.\n\n        Returns:\n            A dict mapping byte values to their frequencies.\n        \"\"\"\n        is_end_of_file = False\n        signs_frequency: dict[int, int] = defaultdict(lambda: 0)\n        while not is_end_of_file:\n            prev_pos = file.tell()\n            sign = file.read(1)\n            curr_pos = file.tell()\n            if prev_pos == curr_pos:\n                is_end_of_file = True\n            else:\n                signs_frequency[int.from_bytes(sign, \"big\")] += 1\n        return signs_frequency\n\n    @staticmethod\n    def _generate_codes(tree: Node) -> dict[int, str]:\n        \"\"\"Generate Huffman codes from the tree.\n\n        Args:\n            tree: The root of the Huffman tree.\n\n        Returns:\n            A dict mapping byte values to their binary code strings.\n        \"\"\"\n        codes: dict[int, str] = {}\n        HuffmanCoding._go_through_tree_and_create_codes(tree, \"\", codes)\n        return codes\n\n    @staticmethod\n    def _create_tree(signs_frequency: dict[int, int]) -> Node:\n        \"\"\"Build a Huffman tree from character frequencies.\n\n        Args:\n            signs_frequency: Mapping of byte values to frequencies.\n\n        Returns:\n            The root node of the constructed Huffman tree.\n        \"\"\"\n        nodes = [\n            Node(frequency=frequency, sign=char_int)\n            for char_int, frequency in signs_frequency.items()\n        ]\n        heapq.heapify(nodes)\n\n        while len(nodes) > 1:\n            left = heapq.heappop(nodes)\n            right = heapq.heappop(nodes)\n            new_node = Node(\n                frequency=left.frequency + right.frequency,\n                left=left,\n                right=right,\n            )\n            heapq.heappush(nodes, new_node)\n\n        return nodes[0]\n\n    @staticmethod\n    def _go_through_tree_and_create_codes(\n        tree: Node, code: str, dict_codes: dict[int, str]\n    ) -> None:\n        \"\"\"Recursively traverse the tree to build code mappings.\n\n        Args:\n            tree: The current node being visited.\n            code: The accumulated bit string for this path.\n            dict_codes: The output dict to populate.\n        \"\"\"\n        if tree.sign is not None:\n            dict_codes[tree.sign] = code\n\n        if tree.left:\n            HuffmanCoding._go_through_tree_and_create_codes(\n                tree.left, code + \"0\", dict_codes\n            )\n\n        if tree.right:\n            HuffmanCoding._go_through_tree_and_create_codes(\n                tree.right, code + \"1\", dict_codes\n            )\n"
  },
  {
    "path": "algorithms/compression/rle_compression.py",
    "content": "\"\"\"\nRun-Length Encoding (RLE)\n\nA simple lossless compression algorithm that encodes consecutive repeated\ncharacters as a count followed by the character. Decompression fully recovers\nthe original data.\n\nReference: https://en.wikipedia.org/wiki/Run-length_encoding\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef encode_rle(data: str) -> str:\n    \"\"\"Compress a string using run-length encoding.\n\n    Args:\n        data: The input string to compress.\n\n    Returns:\n        The RLE-encoded string.\n\n    Examples:\n        >>> encode_rle(\"aaabbc\")\n        '3a2b1c'\n        >>> encode_rle(\"\")\n        ''\n    \"\"\"\n    if not data:\n        return \"\"\n\n    encoded: str = \"\"\n    prev_char: str = \"\"\n    count: int = 1\n\n    for char in data:\n        if char != prev_char:\n            if prev_char:\n                encoded += str(count) + prev_char\n            count = 1\n            prev_char = char\n        else:\n            count += 1\n\n    return encoded + str(count) + prev_char\n\n\ndef decode_rle(data: str) -> str:\n    \"\"\"Decompress a run-length encoded string.\n\n    Args:\n        data: The RLE-encoded string.\n\n    Returns:\n        The decoded original string.\n\n    Examples:\n        >>> decode_rle(\"3a2b1c\")\n        'aaabbc'\n        >>> decode_rle(\"\")\n        ''\n    \"\"\"\n    decoded: str = \"\"\n    count: str = \"\"\n\n    for char in data:\n        if not char.isdigit():\n            decoded += char * int(count)\n            count = \"\"\n        else:\n            count += char\n    return decoded\n"
  },
  {
    "path": "algorithms/data_structures/__init__.py",
    "content": "\"\"\"Reusable data structure implementations.\n\nThis package contains the core data structures used throughout the library.\nEach module provides a self-contained implementation suitable for study.\n\n    >>> from algorithms.data_structures import BinaryHeap, ArrayStack\n\"\"\"\n\n# Tree data structures (moved from tree/ subdirectories in Phase 8)\nfrom algorithms.data_structures.avl_tree import AvlTree\nfrom algorithms.data_structures.b_tree import BTree\nfrom algorithms.data_structures.bst import BST\nfrom algorithms.data_structures.fenwick_tree import Fenwick_Tree\nfrom algorithms.data_structures.graph import DirectedEdge, DirectedGraph, Node\nfrom algorithms.data_structures.hash_table import HashTable, ResizableHashTable\nfrom algorithms.data_structures.heap import AbstractHeap, BinaryHeap\nfrom algorithms.data_structures.iterative_segment_tree import SegmentTree\nfrom algorithms.data_structures.kd_tree import KDTree\nfrom algorithms.data_structures.linked_list import (\n    DoublyLinkedListNode,\n    SinglyLinkedListNode,\n)\nfrom algorithms.data_structures.priority_queue import PriorityQueue, PriorityQueueNode\nfrom algorithms.data_structures.queue import (\n    AbstractQueue,\n    ArrayQueue,\n    LinkedListQueue,\n    QueueNode,\n)\nfrom algorithms.data_structures.red_black_tree import RBTree\nfrom algorithms.data_structures.segment_tree import SegmentTree as SegmentTreeRecursive\nfrom algorithms.data_structures.separate_chaining_hash_table import (\n    SeparateChainingHashTable,\n)\nfrom algorithms.data_structures.sqrt_decomposition import SqrtDecomposition\nfrom algorithms.data_structures.stack import (\n    AbstractStack,\n    ArrayStack,\n    LinkedListStack,\n    StackNode,\n)\nfrom algorithms.data_structures.trie import Trie\nfrom algorithms.data_structures.union_find import Union\n\n__all__ = [\n    \"AbstractHeap\",\n    \"AbstractQueue\",\n    \"AbstractStack\",\n    \"ArrayQueue\",\n    \"ArrayStack\",\n    \"BinaryHeap\",\n    \"DirectedEdge\",\n    \"DirectedGraph\",\n    \"DoublyLinkedListNode\",\n    \"HashTable\",\n    \"LinkedListQueue\",\n    \"LinkedListStack\",\n    \"Node\",\n    \"PriorityQueue\",\n    \"PriorityQueueNode\",\n    \"QueueNode\",\n    \"ResizableHashTable\",\n    \"SeparateChainingHashTable\",\n    \"SinglyLinkedListNode\",\n    \"StackNode\",\n    \"Union\",\n    # Tree data structures\n    \"AvlTree\",\n    \"BTree\",\n    \"BST\",\n    \"Fenwick_Tree\",\n    \"RBTree\",\n    \"SegmentTree\",\n    \"SegmentTreeRecursive\",\n    \"Trie\",\n    \"KDTree\",\n    \"SqrtDecomposition\",\n]\n"
  },
  {
    "path": "algorithms/data_structures/avl_tree.py",
    "content": "\"\"\"Imports TreeNodes\"\"\"\n\nfrom algorithms.common.tree_node import TreeNode\n\n\nclass AvlTree:\n    \"\"\"\n    An avl tree.\n    \"\"\"\n\n    def __init__(self):\n        # Root node of the tree.\n        self.node = None\n        self.height = -1\n        self.balance = 0\n\n    def insert(self, key):\n        \"\"\"\n        Insert new key into node\n        \"\"\"\n        # Create new node\n        node = TreeNode(key)\n        if not self.node:\n            self.node = node\n            self.node.left = AvlTree()\n            self.node.right = AvlTree()\n        elif key < self.node.val:\n            self.node.left.insert(key)\n        elif key > self.node.val:\n            self.node.right.insert(key)\n        self.re_balance()\n\n    def re_balance(self):\n        \"\"\"\n        Re balance tree. After inserting or deleting a node,\n        \"\"\"\n        self.update_heights(recursive=False)\n        self.update_balances(False)\n\n        while self.balance < -1 or self.balance > 1:\n            if self.balance > 1:\n                if self.node.left.balance < 0:\n                    self.node.left.rotate_left()\n                    self.update_heights()\n                    self.update_balances()\n                self.rotate_right()\n                self.update_heights()\n                self.update_balances()\n\n            if self.balance < -1:\n                if self.node.right.balance > 0:\n                    self.node.right.rotate_right()\n                    self.update_heights()\n                    self.update_balances()\n                self.rotate_left()\n                self.update_heights()\n                self.update_balances()\n\n    def update_heights(self, recursive=True):\n        \"\"\"\n        Update tree height\n        \"\"\"\n        if self.node:\n            if recursive:\n                if self.node.left:\n                    self.node.left.update_heights()\n                if self.node.right:\n                    self.node.right.update_heights()\n\n            self.height = 1 + max(self.node.left.height, self.node.right.height)\n        else:\n            self.height = -1\n\n    def update_balances(self, recursive=True):\n        \"\"\"\n        Calculate tree balance factor\n\n        \"\"\"\n        if self.node:\n            if recursive:\n                if self.node.left:\n                    self.node.left.update_balances()\n                if self.node.right:\n                    self.node.right.update_balances()\n\n            self.balance = self.node.left.height - self.node.right.height\n        else:\n            self.balance = 0\n\n    def rotate_right(self):\n        \"\"\"\n        Right rotation\n        \"\"\"\n        new_root = self.node.left.node\n        new_left_sub = new_root.right.node\n        old_root = self.node\n\n        self.node = new_root\n        old_root.left.node = new_left_sub\n        new_root.right.node = old_root\n\n    def rotate_left(self):\n        \"\"\"\n        Left rotation\n        \"\"\"\n        new_root = self.node.right.node\n        new_left_sub = new_root.left.node\n        old_root = self.node\n\n        self.node = new_root\n        old_root.right.node = new_left_sub\n        new_root.left.node = old_root\n\n    def in_order_traverse(self):\n        \"\"\"\n        In-order traversal of the tree\n        \"\"\"\n        result = []\n\n        if not self.node:\n            return result\n\n        result.extend(self.node.left.in_order_traverse())\n        result.append(self.node.val)\n        result.extend(self.node.right.in_order_traverse())\n        return result\n"
  },
  {
    "path": "algorithms/data_structures/b_tree.py",
    "content": "\"\"\"\nB-Tree\n\nA self-balancing tree data structure optimized for disk operations. Each node\n(except root) contains at least t-1 keys and at most 2t-1 keys, where t is the\nminimum degree. The tree grows upward from the root.\n\nReference: https://en.wikipedia.org/wiki/B-tree\n\nComplexity:\n    Time:  O(log n) for search, insert, and delete\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    \"\"\"A node in a B-tree containing keys and child pointers.\n\n    Examples:\n        >>> node = Node()\n        >>> node.keys\n        []\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.keys: list = []\n        self.children: list[Node] = []\n\n    def __repr__(self) -> str:\n        \"\"\"Return a string representation of the node.\n\n        Returns:\n            A string showing the node's keys.\n        \"\"\"\n        return f\"<id_node: {self.keys}>\"\n\n    @property\n    def is_leaf(self) -> bool:\n        \"\"\"Check whether this node is a leaf.\n\n        Returns:\n            True if the node has no children, False otherwise.\n        \"\"\"\n        return len(self.children) == 0\n\n\nclass BTree:\n    \"\"\"A B-tree data structure supporting search, insertion, and deletion.\n\n    Args:\n        t_val: The minimum degree of the B-tree.\n\n    Examples:\n        >>> bt = BTree(2)\n        >>> bt.insert_key(10)\n        >>> bt.find(10)\n        True\n    \"\"\"\n\n    def __init__(self, t_val: int = 2) -> None:\n        self.min_numbers_of_keys = t_val - 1\n        self.max_number_of_keys = 2 * t_val - 1\n        self.root = Node()\n\n    def _split_child(self, parent: Node, child_index: int) -> None:\n        \"\"\"Split a full child node into two nodes.\n\n        Args:\n            parent: The parent node whose child is being split.\n            child_index: The index of the child to split.\n        \"\"\"\n        new_right_child = Node()\n        half_max = self.max_number_of_keys // 2\n        child = parent.children[child_index]\n        middle_key = child.keys[half_max]\n        new_right_child.keys = child.keys[half_max + 1 :]\n        child.keys = child.keys[:half_max]\n\n        if not child.is_leaf:\n            new_right_child.children = child.children[half_max + 1 :]\n            child.children = child.children[: half_max + 1]\n\n        parent.keys.insert(child_index, middle_key)\n        parent.children.insert(child_index + 1, new_right_child)\n\n    def insert_key(self, key: int) -> None:\n        \"\"\"Insert a key into the B-tree.\n\n        Args:\n            key: The key to insert.\n        \"\"\"\n        if len(self.root.keys) >= self.max_number_of_keys:\n            new_root = Node()\n            new_root.children.append(self.root)\n            self.root = new_root\n            self._split_child(new_root, 0)\n            self._insert_to_nonfull_node(self.root, key)\n        else:\n            self._insert_to_nonfull_node(self.root, key)\n\n    def _insert_to_nonfull_node(self, node: Node, key: int) -> None:\n        \"\"\"Insert a key into a non-full node.\n\n        Args:\n            node: The non-full node to insert into.\n            key: The key to insert.\n        \"\"\"\n        i = len(node.keys) - 1\n        while i >= 0 and node.keys[i] >= key:\n            i -= 1\n\n        if node.is_leaf:\n            node.keys.insert(i + 1, key)\n        else:\n            if len(node.children[i + 1].keys) >= self.max_number_of_keys:\n                self._split_child(node, i + 1)\n                if node.keys[i + 1] < key:\n                    i += 1\n            self._insert_to_nonfull_node(node.children[i + 1], key)\n\n    def find(self, key: int) -> bool:\n        \"\"\"Search for a key in the B-tree.\n\n        Args:\n            key: The key to search for.\n\n        Returns:\n            True if the key is found, False otherwise.\n\n        Examples:\n            >>> bt = BTree(2)\n            >>> bt.insert_key(5)\n            >>> bt.find(5)\n            True\n            >>> bt.find(3)\n            False\n        \"\"\"\n        current_node = self.root\n        while True:\n            i = len(current_node.keys) - 1\n            while i >= 0 and current_node.keys[i] > key:\n                i -= 1\n            if i >= 0 and current_node.keys[i] == key:\n                return True\n            if current_node.is_leaf:\n                return False\n            current_node = current_node.children[i + 1]\n\n    def remove_key(self, key: int) -> None:\n        \"\"\"Remove a key from the B-tree.\n\n        Args:\n            key: The key to remove.\n        \"\"\"\n        self._remove_key(self.root, key)\n\n    def _remove_key(self, node: Node, key: int) -> bool:\n        \"\"\"Recursively remove a key from the subtree rooted at node.\n\n        Args:\n            node: The root of the subtree to remove from.\n            key: The key to remove.\n\n        Returns:\n            True if the key was found and removed, False otherwise.\n        \"\"\"\n        try:\n            key_index = node.keys.index(key)\n            if node.is_leaf:\n                node.keys.remove(key)\n            else:\n                self._remove_from_nonleaf_node(node, key_index)\n            return True\n\n        except ValueError:\n            if node.is_leaf:\n                return False\n            else:\n                i = 0\n                number_of_keys = len(node.keys)\n                while i < number_of_keys and key > node.keys[i]:\n                    i += 1\n\n                action_performed = self._repair_tree(node, i)\n                if action_performed:\n                    return self._remove_key(node, key)\n                else:\n                    return self._remove_key(node.children[i], key)\n\n    def _repair_tree(self, node: Node, child_index: int) -> bool:\n        \"\"\"Repair the tree after a deletion to maintain B-tree properties.\n\n        Args:\n            node: The parent node of the child that may need repair.\n            child_index: The index of the child to check.\n\n        Returns:\n            True if a structural repair was performed, False otherwise.\n        \"\"\"\n        child = node.children[child_index]\n        if self.min_numbers_of_keys < len(child.keys) <= self.max_number_of_keys:\n            return False\n\n        if (\n            child_index > 0\n            and len(node.children[child_index - 1].keys) > self.min_numbers_of_keys\n        ):\n            self._rotate_right(node, child_index)\n            return True\n\n        if (\n            child_index < len(node.children) - 1\n            and len(node.children[child_index + 1].keys) > self.min_numbers_of_keys\n        ):\n            self._rotate_left(node, child_index)\n            return True\n\n        if child_index > 0:\n            self._merge(node, child_index - 1, child_index)\n        else:\n            self._merge(node, child_index, child_index + 1)\n\n        return True\n\n    def _rotate_left(self, parent_node: Node, child_index: int) -> None:\n        \"\"\"Take a key from the right sibling and transfer it to the child.\n\n        Args:\n            parent_node: The parent node.\n            child_index: The index of the child receiving the key.\n        \"\"\"\n        new_child_key = parent_node.keys[child_index]\n        new_parent_key = parent_node.children[child_index + 1].keys.pop(0)\n        parent_node.children[child_index].keys.append(new_child_key)\n        parent_node.keys[child_index] = new_parent_key\n\n        if not parent_node.children[child_index + 1].is_leaf:\n            ownerless_child = parent_node.children[child_index + 1].children.pop(0)\n            parent_node.children[child_index].children.append(ownerless_child)\n\n    def _rotate_right(self, parent_node: Node, child_index: int) -> None:\n        \"\"\"Take a key from the left sibling and transfer it to the child.\n\n        Args:\n            parent_node: The parent node.\n            child_index: The index of the child receiving the key.\n        \"\"\"\n        parent_key = parent_node.keys[child_index - 1]\n        new_parent_key = parent_node.children[child_index - 1].keys.pop()\n        parent_node.children[child_index].keys.insert(0, parent_key)\n        parent_node.keys[child_index - 1] = new_parent_key\n\n        if not parent_node.children[child_index - 1].is_leaf:\n            ownerless_child = parent_node.children[child_index - 1].children.pop()\n            parent_node.children[child_index].children.insert(0, ownerless_child)\n\n    def _merge(\n        self, parent_node: Node, to_merge_index: int, transferred_child_index: int\n    ) -> None:\n        \"\"\"Merge two child nodes and a parent key into a single node.\n\n        Args:\n            parent_node: The parent node.\n            to_merge_index: Index of the child that receives the merged data.\n            transferred_child_index: Index of the child being merged in.\n        \"\"\"\n        from_merge_node = parent_node.children.pop(transferred_child_index)\n        parent_key_to_merge = parent_node.keys.pop(to_merge_index)\n        to_merge_node = parent_node.children[to_merge_index]\n        to_merge_node.keys.append(parent_key_to_merge)\n        to_merge_node.keys.extend(from_merge_node.keys)\n\n        if not to_merge_node.is_leaf:\n            to_merge_node.children.extend(from_merge_node.children)\n\n        if parent_node == self.root and not parent_node.keys:\n            self.root = to_merge_node\n\n    def _remove_from_nonleaf_node(\n        self, node: Node, key_index: int\n    ) -> None:\n        \"\"\"Remove a key from a non-leaf node by replacing with predecessor/successor.\n\n        Args:\n            node: The non-leaf node containing the key.\n            key_index: The index of the key to remove.\n        \"\"\"\n        key = node.keys[key_index]\n        left_subtree = node.children[key_index]\n        if len(left_subtree.keys) > self.min_numbers_of_keys:\n            largest_key = self._find_largest_and_delete_in_left_subtree(left_subtree)\n        elif len(node.children[key_index + 1].keys) > self.min_numbers_of_keys:\n            largest_key = self._find_largest_and_delete_in_right_subtree(\n                node.children[key_index + 1]\n            )\n        else:\n            self._merge(node, key_index, key_index + 1)\n            return self._remove_key(node, key)\n\n        node.keys[key_index] = largest_key\n\n    def _find_largest_and_delete_in_left_subtree(self, node: Node) -> int:\n        \"\"\"Find and remove the largest key in the left subtree.\n\n        Args:\n            node: The root of the subtree.\n\n        Returns:\n            The largest key that was removed.\n        \"\"\"\n        if node.is_leaf:\n            return node.keys.pop()\n        else:\n            ch_index = len(node.children) - 1\n            self._repair_tree(node, ch_index)\n            largest_key_in_subtree = self._find_largest_and_delete_in_left_subtree(\n                node.children[len(node.children) - 1]\n            )\n            return largest_key_in_subtree\n\n    def _find_largest_and_delete_in_right_subtree(self, node: Node) -> int:\n        \"\"\"Find and remove the smallest key in the right subtree.\n\n        Args:\n            node: The root of the subtree.\n\n        Returns:\n            The smallest key that was removed.\n        \"\"\"\n        if node.is_leaf:\n            return node.keys.pop(0)\n        else:\n            ch_index = 0\n            self._repair_tree(node, ch_index)\n            largest_key_in_subtree = self._find_largest_and_delete_in_right_subtree(\n                node.children[0]\n            )\n            return largest_key_in_subtree\n\n    def traverse_tree(self) -> list:\n        \"\"\"Traverse the B-tree in order and return all keys.\n\n        Returns:\n            A list of all keys in sorted order.\n\n        Examples:\n            >>> bt = BTree(2)\n            >>> for k in [3, 1, 2]: bt.insert_key(k)\n            >>> bt.traverse_tree()\n            [1, 2, 3]\n        \"\"\"\n        result: list = []\n        self._traverse_tree(self.root, result)\n        return result\n\n    def _traverse_tree(self, node: Node, result: list) -> None:\n        \"\"\"Recursively traverse the subtree and collect keys.\n\n        Args:\n            node: The root of the subtree to traverse.\n            result: The list to append keys to.\n        \"\"\"\n        if node.is_leaf:\n            result.extend(node.keys)\n        else:\n            for i, key in enumerate(node.keys):\n                self._traverse_tree(node.children[i], result)\n                result.append(key)\n            self._traverse_tree(node.children[-1], result)\n"
  },
  {
    "path": "algorithms/data_structures/bst.py",
    "content": "\"\"\"Binary Search Tree implementation.\n\nA BST is a node-based binary tree where each node's left subtree contains\nonly nodes with data less than the node's data, and the right subtree\ncontains only nodes with data greater than the node's data.\n\nOperations and complexities (n = number of nodes):\n    - insert:    O(log n) average, O(n) worst case\n    - search:    O(log n) average, O(n) worst case\n    - size:      O(n)\n    - preorder:  O(n)\n    - inorder:   O(n)\n    - postorder: O(n)\n\"\"\"\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, data: int) -> None:\n        self.data: int = data\n        self.left: Node | None = None\n        self.right: Node | None = None\n\n\nclass BST:\n    def __init__(self) -> None:\n        self.root: Node | None = None\n\n    def get_root(self) -> Node | None:\n        return self.root\n\n    def size(self) -> int:\n        \"\"\"Return the number of nodes in the tree. Complexity: O(n).\"\"\"\n        return self._recur_size(self.root)\n\n    def _recur_size(self, root: Node | None) -> int:\n        if root is None:\n            return 0\n        return 1 + self._recur_size(root.left) + self._recur_size(root.right)\n\n    def search(self, data: int) -> bool:\n        \"\"\"Return True if data exists in the tree. Complexity: O(log n) average.\"\"\"\n        return self._recur_search(self.root, data)\n\n    def _recur_search(self, root: Node | None, data: int) -> bool:\n        if root is None:\n            return False\n        if root.data == data:\n            return True\n        elif data > root.data:\n            return self._recur_search(root.right, data)\n        else:\n            return self._recur_search(root.left, data)\n\n    def insert(self, data: int) -> bool:\n        \"\"\"Insert data into the tree.\n\n        Return False if data already exists.\n        Complexity: O(log n) average.\n        \"\"\"\n        if self.root:\n            return self._recur_insert(self.root, data)\n        else:\n            self.root = Node(data)\n            return True\n\n    def _recur_insert(self, root: Node, data: int) -> bool:\n        if root.data == data:\n            return False\n        elif data < root.data:\n            if root.left:\n                return self._recur_insert(root.left, data)\n            else:\n                root.left = Node(data)\n                return True\n        else:\n            if root.right:\n                return self._recur_insert(root.right, data)\n            else:\n                root.right = Node(data)\n                return True\n\n    def preorder(self, root: Node | None) -> list[int]:\n        \"\"\"Return list of node values in preorder (root, left, right).\"\"\"\n        result: list[int] = []\n        if root:\n            result.append(root.data)\n            result.extend(self.preorder(root.left))\n            result.extend(self.preorder(root.right))\n        return result\n\n    def inorder(self, root: Node | None) -> list[int]:\n        \"\"\"Return list of node values in inorder (left, root, right).\"\"\"\n        result: list[int] = []\n        if root:\n            result.extend(self.inorder(root.left))\n            result.append(root.data)\n            result.extend(self.inorder(root.right))\n        return result\n\n    def postorder(self, root: Node | None) -> list[int]:\n        \"\"\"Return list of node values in postorder (left, right, root).\"\"\"\n        result: list[int] = []\n        if root:\n            result.extend(self.postorder(root.left))\n            result.extend(self.postorder(root.right))\n            result.append(root.data)\n        return result\n"
  },
  {
    "path": "algorithms/data_structures/fenwick_tree.py",
    "content": "\"\"\"\nFenwick Tree / Binary Indexed Tree\n\nConsider we have an array arr[0 . . . n-1]. We would like to\n1. Compute the sum of the first i elements.\n2. Modify the value of a specified element of the array\n   arr[i] = x where 0 <= i <= n-1.\n\nA simple solution is to run a loop from 0 to i-1 and calculate\nthe sum of the elements. To update a value, simply do\narr[i] = x. The first operation takes O(n) time and the second\noperation takes O(1) time. Another simple solution is to create\nan extra array and store the sum of the first i-th elements at\nthe i-th index in this new array. The sum of a given range can\nnow be calculated in O(1) time, but the update operation takes\nO(n) time now. This works well if there are a large number of\nquery operations but a very few number of update operations.\n\n\nThere are two solutions that can perform both the query and\nupdate operations in O(logn) time.\n1. Fenwick Tree\n2. Segment Tree\n\nCompared with Segment Tree, Binary Indexed Tree requires less\nspace and is easier to implement.\n\"\"\"\n\n\nclass Fenwick_Tree:  # noqa: N801\n    def __init__(self, freq):\n        self.arr = freq\n        self.n = len(freq)\n\n    def get_sum(self, bit_tree, i):\n        \"\"\"\n        Returns sum of arr[0..index]. This function assumes\n        that the array is preprocessed and partial sums of\n        array elements are stored in bit_tree[].\n        \"\"\"\n\n        s = 0\n\n        # index in bit_tree[] is 1 more than the index in arr[]\n        i = i + 1\n\n        # Traverse ancestors of bit_tree[index]\n        while i > 0:\n            # Add current element of bit_tree to sum\n            s += bit_tree[i]\n\n            # Move index to parent node in getSum View\n            i -= i & (-i)\n        return s\n\n    def update_bit(self, bit_tree, i, v):\n        \"\"\"\n        Updates a node in Binary Index Tree (bit_tree) at\n        given index in bit_tree. The given value 'val' is\n        added to bit_tree[i] and all of its ancestors in tree.\n        \"\"\"\n\n        # index in bit_ree[] is 1 more than the index in arr[]\n        i += 1\n\n        # Traverse all ancestors and add 'val'\n        while i <= self.n:\n            # Add 'val' to current node of bit_tree\n            bit_tree[i] += v\n\n            # Update index to that of parent in update View\n            i += i & (-i)\n\n    def construct(self):\n        \"\"\"\n        Constructs and returns a Binary Indexed Tree for given\n        array of size n.\n        \"\"\"\n\n        # Create and initialize bit_ree[] as 0\n        bit_tree = [0] * (self.n + 1)\n\n        # Store the actual values in bit_ree[] using update()\n        for i in range(self.n):\n            self.update_bit(bit_tree, i, self.arr[i])\n\n        return bit_tree\n"
  },
  {
    "path": "algorithms/data_structures/graph.py",
    "content": "\"\"\"\nGraph Data Structures\n\nReusable classes for representing nodes, directed edges and directed graphs.\nThese can be shared across graph algorithms.\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    \"\"\"A node (vertex) in a graph.\"\"\"\n\n    def __init__(self, name: str) -> None:\n        self.name = name\n\n    @staticmethod\n    def get_name(obj: object) -> str:\n        \"\"\"Return the name of *obj* whether it is a Node or a string.\n\n        Args:\n            obj: A Node instance or a string.\n\n        Returns:\n            The string name.\n        \"\"\"\n        if isinstance(obj, Node):\n            return obj.name\n        if isinstance(obj, str):\n            return obj\n        return \"\"\n\n    def __eq__(self, obj: object) -> bool:\n        return self.name == self.get_name(obj)\n\n    def __repr__(self) -> str:\n        return self.name\n\n    def __hash__(self) -> int:\n        return hash(self.name)\n\n    def __ne__(self, obj: object) -> bool:\n        return self.name != self.get_name(obj)\n\n    def __lt__(self, obj: object) -> bool:\n        return self.name < self.get_name(obj)\n\n    def __le__(self, obj: object) -> bool:\n        return self.name <= self.get_name(obj)\n\n    def __gt__(self, obj: object) -> bool:\n        return self.name > self.get_name(obj)\n\n    def __ge__(self, obj: object) -> bool:\n        return self.name >= self.get_name(obj)\n\n    def __bool__(self) -> bool:\n        return bool(self.name)\n\n\nclass DirectedEdge:\n    \"\"\"A directed edge connecting two nodes.\"\"\"\n\n    def __init__(self, node_from: Node, node_to: Node) -> None:\n        self.source = node_from\n        self.target = node_to\n\n    def __eq__(self, obj: object) -> bool:\n        if isinstance(obj, DirectedEdge):\n            return obj.source == self.source and obj.target == self.target\n        return False\n\n    def __repr__(self) -> str:\n        return f\"({self.source} -> {self.target})\"\n\n\nclass DirectedGraph:\n    \"\"\"A directed graph storing nodes, edges and an adjacency list.\"\"\"\n\n    def __init__(self, load_dict: dict[str, list[str]] | None = None) -> None:\n        \"\"\"Build a directed graph, optionally from a dictionary.\n\n        Args:\n            load_dict: Optional adjacency dict ``{vertex: [neighbours]}``.\n        \"\"\"\n        if load_dict is None:\n            load_dict = {}\n        self.nodes: list[Node] = []\n        self.edges: list[DirectedEdge] = []\n        self.adjacency_list: dict[Node, list[Node]] = {}\n\n        if load_dict and isinstance(load_dict, dict):\n            for vertex in load_dict:\n                node_from = self.add_node(vertex)\n                self.adjacency_list[node_from] = []\n                for neighbor in load_dict[vertex]:\n                    node_to = self.add_node(neighbor)\n                    self.adjacency_list[node_from].append(node_to)\n                    self.add_edge(vertex, neighbor)\n\n    def add_node(self, node_name: str) -> Node:\n        \"\"\"Add a named node (or return it if it already exists).\n\n        Args:\n            node_name: Name of the node.\n\n        Returns:\n            The Node instance.\n        \"\"\"\n        try:\n            return self.nodes[self.nodes.index(node_name)]\n        except ValueError:\n            node = Node(node_name)\n            self.nodes.append(node)\n            return node\n\n    def add_edge(self, node_name_from: str, node_name_to: str) -> None:\n        \"\"\"Add a directed edge between two named nodes.\n\n        Args:\n            node_name_from: Source node name.\n            node_name_to: Target node name.\n        \"\"\"\n        try:\n            node_from = self.nodes[self.nodes.index(node_name_from)]\n            node_to = self.nodes[self.nodes.index(node_name_to)]\n            self.edges.append(DirectedEdge(node_from, node_to))\n        except ValueError:\n            pass\n"
  },
  {
    "path": "algorithms/data_structures/hash_table.py",
    "content": "\"\"\"\nHash Table (Open Addressing)\n\nHash map implementation using open addressing with linear probing\nfor collision resolution. Includes a resizable variant that doubles\ncapacity when the load factor reaches two-thirds.\n\nReference: https://en.wikipedia.org/wiki/Open_addressing\n\nComplexity:\n    Time:  O(1) average for put/get/del, O(n) worst case\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass HashTable:\n    \"\"\"Hash table using open addressing with linear probing.\n\n    Examples:\n        >>> ht = HashTable(10)\n        >>> ht.put(1, 'one')\n        >>> ht.get(1)\n        'one'\n    \"\"\"\n\n    _empty = object()\n    _deleted = object()\n\n    def __init__(self, size: int = 11) -> None:\n        \"\"\"Initialize the hash table.\n\n        Args:\n            size: Number of slots in the underlying array.\n        \"\"\"\n        self.size = size\n        self._len = 0\n        self._keys: list[object] = [self._empty] * size\n        self._values: list[object] = [self._empty] * size\n\n    def put(self, key: int, value: object) -> None:\n        \"\"\"Insert or update a key-value pair.\n\n        Args:\n            key: The key to insert.\n            value: The value associated with the key.\n\n        Raises:\n            ValueError: If the table is full.\n        \"\"\"\n        initial_hash = hash_ = self.hash(key)\n\n        while True:\n            if self._keys[hash_] is self._empty or self._keys[hash_] is self._deleted:\n                self._keys[hash_] = key\n                self._values[hash_] = value\n                self._len += 1\n                return\n            elif self._keys[hash_] == key:\n                self._keys[hash_] = key\n                self._values[hash_] = value\n                return\n\n            hash_ = self._rehash(hash_)\n\n            if initial_hash == hash_:\n                raise ValueError(\"Table is full\")\n\n    def get(self, key: int) -> object | None:\n        \"\"\"Retrieve the value for a given key.\n\n        Args:\n            key: The key to look up.\n\n        Returns:\n            The value associated with the key, or None if not found.\n        \"\"\"\n        initial_hash = hash_ = self.hash(key)\n        while True:\n            if self._keys[hash_] is self._empty:\n                return None\n            elif self._keys[hash_] == key:\n                return self._values[hash_]\n\n            hash_ = self._rehash(hash_)\n            if initial_hash == hash_:\n                return None\n\n    def del_(self, key: int) -> None:\n        \"\"\"Delete a key-value pair.\n\n        Args:\n            key: The key to delete.\n        \"\"\"\n        initial_hash = hash_ = self.hash(key)\n        while True:\n            if self._keys[hash_] is self._empty:\n                return None\n            elif self._keys[hash_] == key:\n                self._keys[hash_] = self._deleted\n                self._values[hash_] = self._deleted\n                self._len -= 1\n                return\n\n            hash_ = self._rehash(hash_)\n            if initial_hash == hash_:\n                return None\n\n    def hash(self, key: int) -> int:\n        \"\"\"Compute the hash index for a key.\n\n        Args:\n            key: The key to hash.\n\n        Returns:\n            Index into the internal array.\n        \"\"\"\n        return key % self.size\n\n    def _rehash(self, old_hash: int) -> int:\n        \"\"\"Linear probing rehash.\n\n        Args:\n            old_hash: The previous hash index.\n\n        Returns:\n            Next index to probe.\n        \"\"\"\n        return (old_hash + 1) % self.size\n\n    def __getitem__(self, key: int) -> object | None:\n        return self.get(key)\n\n    def __delitem__(self, key: int) -> None:\n        return self.del_(key)\n\n    def __setitem__(self, key: int, value: object) -> None:\n        self.put(key, value)\n\n    def __len__(self) -> int:\n        return self._len\n\n\nclass ResizableHashTable(HashTable):\n    \"\"\"Hash table that automatically doubles in size when load exceeds 2/3.\n\n    Examples:\n        >>> rht = ResizableHashTable()\n        >>> rht.put(1, 'a')\n        >>> rht.get(1)\n        'a'\n    \"\"\"\n\n    MIN_SIZE = 8\n\n    def __init__(self) -> None:\n        super().__init__(self.MIN_SIZE)\n\n    def put(self, key: int, value: object) -> None:\n        \"\"\"Insert or update, resizing if load factor exceeds two-thirds.\n\n        Args:\n            key: The key to insert.\n            value: The value associated with the key.\n        \"\"\"\n        super().put(key, value)\n        if len(self) >= (self.size * 2) / 3:\n            self._resize()\n\n    def _resize(self) -> None:\n        \"\"\"Double the table size and rehash all existing entries.\"\"\"\n        keys, values = self._keys, self._values\n        self.size *= 2\n        self._len = 0\n        self._keys = [self._empty] * self.size\n        self._values = [self._empty] * self.size\n        for key, value in zip(keys, values, strict=False):\n            if key is not self._empty and key is not self._deleted:\n                self.put(key, value)\n"
  },
  {
    "path": "algorithms/data_structures/heap.py",
    "content": "r\"\"\"\nBinary Heap\n\nA min heap is a complete binary tree where each node is smaller than\nits children. The root is the minimum element. Uses an array\nrepresentation with index 0 as a sentinel.\n\nReference: https://en.wikipedia.org/wiki/Binary_heap\n\nComplexity:\n    Time:  O(log n) for insert and remove_min\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom abc import ABCMeta, abstractmethod\n\n\nclass AbstractHeap(metaclass=ABCMeta):\n    \"\"\"Abstract base class for binary heap implementations.\"\"\"\n\n    def __init__(self) -> None:  # noqa: B027\n        \"\"\"Initialize the abstract heap.\"\"\"\n\n    @abstractmethod\n    def perc_up(self, index: int) -> None:\n        \"\"\"Percolate element up to restore heap property.\n\n        Args:\n            index: Index of the element to percolate up.\n        \"\"\"\n\n    @abstractmethod\n    def insert(self, val: int) -> None:\n        \"\"\"Insert a value into the heap.\n\n        Args:\n            val: The value to insert.\n        \"\"\"\n\n    @abstractmethod\n    def perc_down(self, index: int) -> None:\n        \"\"\"Percolate element down to restore heap property.\n\n        Args:\n            index: Index of the element to percolate down.\n        \"\"\"\n\n    @abstractmethod\n    def min_child(self, index: int) -> int:\n        \"\"\"Return the index of the smaller child.\n\n        Args:\n            index: Index of the parent node.\n\n        Returns:\n            Index of the smaller child.\n        \"\"\"\n\n    @abstractmethod\n    def remove_min(self) -> int:\n        \"\"\"Remove and return the minimum element.\n\n        Returns:\n            The minimum value in the heap.\n        \"\"\"\n\n\nclass BinaryHeap(AbstractHeap):\n    \"\"\"Min binary heap using array representation.\n\n    Examples:\n        >>> heap = BinaryHeap()\n        >>> heap.insert(5)\n        >>> heap.insert(3)\n        >>> heap.remove_min()\n        3\n    \"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize the binary heap with a sentinel at index 0.\"\"\"\n        self.current_size: int = 0\n        self.heap: list[int] = [0]\n\n    def perc_up(self, index: int) -> None:\n        \"\"\"Percolate element up to maintain the min-heap invariant.\n\n        Args:\n            index: Index of the element to percolate up.\n        \"\"\"\n        while index // 2 > 0:\n            if self.heap[index] < self.heap[index // 2]:\n                self.heap[index], self.heap[index // 2] = (\n                    self.heap[index // 2],\n                    self.heap[index],\n                )\n                index = index // 2\n            else:\n                break\n\n    def insert(self, val: int) -> None:\n        \"\"\"Insert a value into the heap.\n\n        Args:\n            val: The value to insert.\n        \"\"\"\n        self.heap.append(val)\n        self.current_size = self.current_size + 1\n        self.perc_up(self.current_size)\n\n    def min_child(self, index: int) -> int:\n        \"\"\"Return the index of the smaller child of a parent node.\n\n        Args:\n            index: Index of the parent node.\n\n        Returns:\n            Index of the smaller child.\n        \"\"\"\n        if 2 * index + 1 > self.current_size:\n            return 2 * index\n        if self.heap[2 * index] > self.heap[2 * index + 1]:\n            return 2 * index + 1\n        return 2 * index\n\n    def perc_down(self, index: int) -> None:\n        \"\"\"Percolate element down to maintain the min-heap invariant.\n\n        Args:\n            index: Index of the element to percolate down.\n        \"\"\"\n        while 2 * index <= self.current_size:\n            smaller_child = self.min_child(index)\n            if self.heap[smaller_child] < self.heap[index]:\n                self.heap[smaller_child], self.heap[index] = (\n                    self.heap[index],\n                    self.heap[smaller_child],\n                )\n            index = smaller_child\n\n    def remove_min(self) -> int:\n        \"\"\"Remove and return the minimum element from the heap.\n\n        Returns:\n            The minimum value.\n        \"\"\"\n        ret = self.heap[1]\n        self.heap[1] = self.heap[self.current_size]\n        self.current_size = self.current_size - 1\n        self.heap.pop()\n        self.perc_down(1)\n        return ret\n"
  },
  {
    "path": "algorithms/data_structures/iterative_segment_tree.py",
    "content": "\"\"\"\nSegmentTree creates a segment tree with a given array and a \"commutative\" function,\nthis non-recursive version uses less memory than the recursive version and include:\n1. range queries in log(N) time\n2. update an element in log(N) time\nthe function should be commutative and takes 2 values and returns the same type value\n\nExamples -\nmytree = SegmentTree([2, 4, 5, 3, 4],max)\nprint(mytree.query(2, 4))\nmytree.update(3, 6)\nprint(mytree.query(0, 3)) ...\n\nmytree = SegmentTree([4, 5, 2, 3, 4, 43, 3], lambda a, b: a + b)\nprint(mytree.query(0, 6))\nmytree.update(2, -10)\nprint(mytree.query(0, 6)) ...\n\nmytree = SegmentTree([(1, 2), (4, 6), (4, 5)], lambda a, b: (a[0] + b[0], a[1] + b[1]))\nprint(mytree.query(0, 2))\nmytree.update(2, (-1, 2))\nprint(mytree.query(0, 2)) ...\n\"\"\"\n\n\nclass SegmentTree:\n    def __init__(self, arr, function):\n        self.tree = [None for _ in range(len(arr))] + arr\n        self.size = len(arr)\n        self.fn = function\n        self.build_tree()\n\n    def build_tree(self):\n        for i in range(self.size - 1, 0, -1):\n            self.tree[i] = self.fn(self.tree[i * 2], self.tree[i * 2 + 1])\n\n    def update(self, p, v):\n        p += self.size\n        self.tree[p] = v\n        while p > 1:\n            p = p // 2\n            self.tree[p] = self.fn(self.tree[p * 2], self.tree[p * 2 + 1])\n\n    def query(self, left, r):\n        left, r = left + self.size, r + self.size\n        res = None\n        while left <= r:\n            if left % 2 == 1:\n                res = self.tree[left] if res is None else self.fn(res, self.tree[left])\n            if r % 2 == 0:\n                res = self.tree[r] if res is None else self.fn(res, self.tree[r])\n            left, r = (left + 1) // 2, (r - 1) // 2\n        return res\n"
  },
  {
    "path": "algorithms/data_structures/kd_tree.py",
    "content": "\"\"\"KD-tree — a space-partitioning tree for k-dimensional points.\n\nSupports efficient nearest-neighbour and range queries.\n\nInspired by PR #915 (gjones1077).\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\nfrom typing import Any\n\n\nclass KDNode:\n    \"\"\"A single node in a KD-tree.\"\"\"\n\n    __slots__ = (\"point\", \"left\", \"right\", \"axis\")\n\n    def __init__(\n        self,\n        point: tuple[float, ...],\n        left: KDNode | None = None,\n        right: KDNode | None = None,\n        axis: int = 0,\n    ) -> None:\n        self.point = point\n        self.left = left\n        self.right = right\n        self.axis = axis\n\n\nclass KDTree:\n    \"\"\"A k-dimensional tree built from a list of points.\n\n    >>> tree = KDTree([(2, 3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)])\n    >>> tree.nearest((9, 2))\n    (8, 1)\n    \"\"\"\n\n    def __init__(self, points: list[tuple[float, ...]]) -> None:\n        self.k = len(points[0]) if points else 0\n        self.root = self._build(list(points), depth=0)\n\n    def _build(self, points: list[tuple[float, ...]], depth: int) -> KDNode | None:\n        if not points:\n            return None\n        axis = depth % self.k\n        points.sort(key=lambda p: p[axis])\n        mid = len(points) // 2\n        return KDNode(\n            point=points[mid],\n            left=self._build(points[:mid], depth + 1),\n            right=self._build(points[mid + 1 :], depth + 1),\n            axis=axis,\n        )\n\n    def nearest(self, target: tuple[float, ...]) -> tuple[float, ...]:\n        \"\"\"Return the point closest to *target*.\"\"\"\n        best: list[Any] = [None, math.inf]\n        self._nearest(self.root, target, best)\n        return best[0]\n\n    def _nearest(\n        self,\n        node: KDNode | None,\n        target: tuple[float, ...],\n        best: list[Any],\n    ) -> None:\n        if node is None:\n            return\n        dist = _sq_dist(node.point, target)\n        if dist < best[1]:\n            best[0], best[1] = node.point, dist\n        axis = node.axis\n        diff = target[axis] - node.point[axis]\n        close, away = (node.left, node.right) if diff <= 0 else (node.right, node.left)\n        self._nearest(close, target, best)\n        if diff * diff < best[1]:\n            self._nearest(away, target, best)\n\n\ndef _sq_dist(a: tuple[float, ...], b: tuple[float, ...]) -> float:\n    return sum((x - y) ** 2 for x, y in zip(a, b, strict=False))\n"
  },
  {
    "path": "algorithms/data_structures/linked_list.py",
    "content": "\"\"\"\nLinked List Node Definitions\n\nBasic node classes for singly and doubly linked lists, serving as foundational\nbuilding blocks for linked list algorithms.\n\nReference: https://en.wikipedia.org/wiki/Linked_list\n\nComplexity:\n    Time:  O(1) for node creation\n    Space: O(1) per node\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass SinglyLinkedListNode:\n    \"\"\"A node in a singly linked list.\n\n    Attributes:\n        value: The value stored in the node.\n        next: Reference to the next node, or None.\n    \"\"\"\n\n    def __init__(self, value: object) -> None:\n        self.value = value\n        self.next: SinglyLinkedListNode | None = None\n\n\nclass DoublyLinkedListNode:\n    \"\"\"A node in a doubly linked list.\n\n    Attributes:\n        value: The value stored in the node.\n        next: Reference to the next node, or None.\n        prev: Reference to the previous node, or None.\n    \"\"\"\n\n    def __init__(self, value: object) -> None:\n        self.value = value\n        self.next: DoublyLinkedListNode | None = None\n        self.prev: DoublyLinkedListNode | None = None\n"
  },
  {
    "path": "algorithms/data_structures/priority_queue.py",
    "content": "\"\"\"\nPriority Queue (Linear Array)\n\nA priority queue implementation using a sorted linear array. Elements\nare inserted in order so that extraction of the minimum is O(1).\n\nReference: https://en.wikipedia.org/wiki/Priority_queue\n\nComplexity:\n    Time:  O(n) for push, O(1) for pop\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport itertools\nfrom collections.abc import Iterable\nfrom typing import Any\n\n\nclass PriorityQueueNode:\n    \"\"\"A node holding data and its priority.\n\n    Args:\n        data: The stored value.\n        priority: The priority of this node.\n    \"\"\"\n\n    def __init__(self, data: Any, priority: Any) -> None:\n        self.data = data\n        self.priority = priority\n\n    def __repr__(self) -> str:\n        \"\"\"Return a string representation of the node.\n\n        Returns:\n            Formatted string with data and priority.\n        \"\"\"\n        return f\"{self.data}: {self.priority}\"\n\n\nclass PriorityQueue:\n    \"\"\"Priority queue backed by a sorted linear array.\n\n    Examples:\n        >>> pq = PriorityQueue([3, 1, 2])\n        >>> pq.pop()\n        1\n        >>> pq.size()\n        2\n    \"\"\"\n\n    def __init__(\n        self,\n        items: Iterable[Any] | None = None,\n        priorities: Iterable[Any] | None = None,\n    ) -> None:\n        \"\"\"Create a priority queue, optionally from items and priorities.\n\n        Args:\n            items: Initial items to insert.\n            priorities: Corresponding priorities; defaults to item values.\n        \"\"\"\n        self.priority_queue_list: list[PriorityQueueNode] = []\n        if items is None:\n            return\n        if priorities is None:\n            priorities = itertools.repeat(None)\n        for item, priority in zip(items, priorities, strict=False):\n            self.push(item, priority=priority)\n\n    def __repr__(self) -> str:\n        \"\"\"Return a string representation of the priority queue.\n\n        Returns:\n            Formatted string.\n        \"\"\"\n        return f\"PriorityQueue({self.priority_queue_list!r})\"\n\n    def size(self) -> int:\n        \"\"\"Return the number of elements in the queue.\n\n        Returns:\n            The queue size.\n        \"\"\"\n        return len(self.priority_queue_list)\n\n    def push(self, item: Any, priority: Any = None) -> None:\n        \"\"\"Insert an item with the given priority.\n\n        Args:\n            item: The value to insert.\n            priority: Priority value; defaults to the item itself.\n        \"\"\"\n        priority = item if priority is None else priority\n        node = PriorityQueueNode(item, priority)\n        for index, current in enumerate(self.priority_queue_list):\n            if current.priority < node.priority:\n                self.priority_queue_list.insert(index, node)\n                return\n        self.priority_queue_list.append(node)\n\n    def pop(self) -> Any:\n        \"\"\"Remove and return the item with the lowest priority.\n\n        Returns:\n            The data of the lowest-priority node.\n        \"\"\"\n        return self.priority_queue_list.pop().data\n"
  },
  {
    "path": "algorithms/data_structures/queue.py",
    "content": "\"\"\"\nQueue Abstract Data Type\n\nImplementations of the queue ADT using both a fixed-size array and a\nlinked list. Both support enqueue, dequeue, peek, is_empty, len, and iter.\n\nReference: https://en.wikipedia.org/wiki/Queue_(abstract_data_type)\n\nComplexity:\n    Time:  O(1) for enqueue/dequeue/peek (amortized for ArrayQueue)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom abc import ABCMeta, abstractmethod\nfrom collections.abc import Iterator\n\n\nclass AbstractQueue(metaclass=ABCMeta):\n    \"\"\"Abstract base class for queue implementations.\"\"\"\n\n    def __init__(self) -> None:\n        self._size = 0\n\n    def __len__(self) -> int:\n        return self._size\n\n    def is_empty(self) -> bool:\n        \"\"\"Check if the queue is empty.\n\n        Returns:\n            True if the queue has no elements.\n        \"\"\"\n        return self._size == 0\n\n    @abstractmethod\n    def enqueue(self, value: object) -> None:\n        pass\n\n    @abstractmethod\n    def dequeue(self) -> object:\n        pass\n\n    @abstractmethod\n    def peek(self) -> object:\n        pass\n\n    @abstractmethod\n    def __iter__(self) -> Iterator[object]:\n        pass\n\n\nclass ArrayQueue(AbstractQueue):\n    \"\"\"Queue implemented with a dynamic array.\n\n    Examples:\n        >>> q = ArrayQueue()\n        >>> q.enqueue(1)\n        >>> q.dequeue()\n        1\n    \"\"\"\n\n    def __init__(self, capacity: int = 10) -> None:\n        \"\"\"Initialize with a fixed-capacity array.\n\n        Args:\n            capacity: Initial capacity of the underlying array.\n        \"\"\"\n        super().__init__()\n        self._array: list[object | None] = [None] * capacity\n        self._front = 0\n        self._rear = 0\n\n    def __iter__(self) -> Iterator[object]:\n        probe = self._front\n        while True:\n            if probe == self._rear:\n                return\n            yield self._array[probe]\n            probe += 1\n\n    def enqueue(self, value: object) -> None:\n        \"\"\"Add an item to the rear of the queue.\n\n        Args:\n            value: The value to enqueue.\n        \"\"\"\n        if self._rear == len(self._array):\n            self._expand()\n        self._array[self._rear] = value\n        self._rear += 1\n        self._size += 1\n\n    def dequeue(self) -> object:\n        \"\"\"Remove and return the front item.\n\n        Returns:\n            The front element.\n\n        Raises:\n            IndexError: If the queue is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Queue is empty\")\n        value = self._array[self._front]\n        self._array[self._front] = None\n        self._front += 1\n        self._size -= 1\n        return value\n\n    def peek(self) -> object:\n        \"\"\"Return the front element without removing it.\n\n        Returns:\n            The front element.\n\n        Raises:\n            IndexError: If the queue is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Queue is empty\")\n        return self._array[self._front]\n\n    def _expand(self) -> None:\n        \"\"\"Double the size of the underlying array.\"\"\"\n        self._array += [None] * len(self._array)\n\n\nclass QueueNode:\n    \"\"\"A single node in a linked-list-based queue.\"\"\"\n\n    def __init__(self, value: object) -> None:\n        self.value = value\n        self.next: QueueNode | None = None\n\n\nclass LinkedListQueue(AbstractQueue):\n    \"\"\"Queue implemented with a singly linked list.\n\n    Examples:\n        >>> q = LinkedListQueue()\n        >>> q.enqueue(1)\n        >>> q.dequeue()\n        1\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        self._front: QueueNode | None = None\n        self._rear: QueueNode | None = None\n\n    def __iter__(self) -> Iterator[object]:\n        probe = self._front\n        while True:\n            if probe is None:\n                return\n            yield probe.value\n            probe = probe.next\n\n    def enqueue(self, value: object) -> None:\n        \"\"\"Add an item to the rear of the queue.\n\n        Args:\n            value: The value to enqueue.\n        \"\"\"\n        node = QueueNode(value)\n        if self._front is None:\n            self._front = node\n            self._rear = node\n        else:\n            self._rear.next = node\n            self._rear = node\n        self._size += 1\n\n    def dequeue(self) -> object:\n        \"\"\"Remove and return the front item.\n\n        Returns:\n            The front element.\n\n        Raises:\n            IndexError: If the queue is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Queue is empty\")\n        value = self._front.value\n        if self._front is self._rear:\n            self._front = None\n            self._rear = None\n        else:\n            self._front = self._front.next\n        self._size -= 1\n        return value\n\n    def peek(self) -> object:\n        \"\"\"Return the front element without removing it.\n\n        Returns:\n            The front element.\n\n        Raises:\n            IndexError: If the queue is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Queue is empty\")\n        return self._front.value\n"
  },
  {
    "path": "algorithms/data_structures/red_black_tree.py",
    "content": "\"\"\"\nImplementation of Red-Black tree.\n\"\"\"\n\n\nclass RBNode:\n    def __init__(self, val, is_red, parent=None, left=None, right=None):\n        self.val = val\n        self.parent = parent\n        self.left = left\n        self.right = right\n        self.color = is_red\n\n\nclass RBTree:\n    def __init__(self):\n        self.root = None\n\n    def left_rotate(self, node):\n        # set the node as the left child node of the current node's right node\n        right_node = node.right\n        if right_node is None:\n            return\n        else:\n            # right node's left node become the right node of current node\n            node.right = right_node.left\n            if right_node.left is not None:\n                right_node.left.parent = node\n            right_node.parent = node.parent\n            # check the parent case\n            if node.parent is None:\n                self.root = right_node\n            elif node is node.parent.left:\n                node.parent.left = right_node\n            else:\n                node.parent.right = right_node\n            right_node.left = node\n            node.parent = right_node\n\n    def right_rotate(self, node):\n        # set the node as the right child node of the current node's left node\n        left_node = node.left\n        if left_node is None:\n            return\n        else:\n            # left node's right  node become the left node of current node\n            node.left = left_node.right\n            if left_node.right is not None:\n                left_node.right.parent = node\n            left_node.parent = node.parent\n            # check the parent case\n            if node.parent is None:\n                self.root = left_node\n            elif node is node.parent.left:\n                node.parent.left = left_node\n            else:\n                node.parent.right = left_node\n            left_node.right = node\n            node.parent = left_node\n\n    def insert(self, node):\n        # the inserted node's color is default is red\n        root = self.root\n        insert_node_parent = None\n        # find the position of inserted node\n        while root is not None:\n            insert_node_parent = root\n            root = root.right if insert_node_parent.val < node.val else root.left\n        # set the n ode's parent node\n        node.parent = insert_node_parent\n        if insert_node_parent is None:\n            # case 1  inserted tree is null\n            self.root = node\n        elif insert_node_parent.val > node.val:\n            # case 2 not null and find left or right\n            insert_node_parent.left = node\n        else:\n            insert_node_parent.right = node\n        node.left = None\n        node.right = None\n        node.color = 1\n        # fix the tree to\n        self.fix_insert(node)\n\n    def fix_insert(self, node):\n        # case 1 the parent is null, then set the inserted node as root and color = 0\n        if node.parent is None:\n            node.color = 0\n            self.root = node\n            return\n            # case 2 the parent color is black, do nothing\n        # case 3 the parent color is red\n        while node.parent and node.parent.color == 1:\n            if node.parent is node.parent.parent.left:\n                uncle_node = node.parent.parent.right\n                if uncle_node and uncle_node.color == 1:\n                    # case 3.1 the uncle node is red\n                    # then set parent and uncle color is black and grandparent is red\n                    # then node => node.parent\n                    node.parent.color = 0\n                    node.parent.parent.right.color = 0\n                    node.parent.parent.color = 1\n                    node = node.parent.parent\n                    continue\n                elif node is node.parent.right:\n                    # case 3.2 the uncle node is black or null,\n                    # and the node is right of parent\n                    # then set his parent node is current node\n                    # left rotate the node and continue the next\n                    node = node.parent\n                    self.left_rotate(node)\n                # case 3.3 the uncle node is black and parent node is left\n                # then parent node set black and grandparent set red\n                node.parent.color = 0\n                node.parent.parent.color = 1\n                self.right_rotate(node.parent.parent)\n            else:\n                uncle_node = node.parent.parent.left\n                if uncle_node and uncle_node.color == 1:\n                    # case 3.1 the uncle node is red\n                    # then set parent and uncle color is black and grandparent is red\n                    # then node => node.parent\n                    node.parent.color = 0\n                    node.parent.parent.left.color = 0\n                    node.parent.parent.color = 1\n                    node = node.parent.parent\n                    continue\n                elif node is node.parent.left:\n                    # case 3.2 the uncle node is black or null,\n                    # and the node is right of parent\n                    # then set his parent node is current node\n                    # left rotate the node and continue the next\n                    node = node.parent\n                    self.right_rotate(node)\n                # case 3.3 the uncle node is black and parent node is left\n                # then parent node set black and grandparent set red\n                node.parent.color = 0\n                node.parent.parent.color = 1\n                self.left_rotate(node.parent.parent)\n        self.root.color = 0\n\n    def transplant(self, node_u, node_v):\n        \"\"\"\n        replace u with v\n        :param node_u: replaced node\n        :param node_v:\n        :return: None\n        \"\"\"\n        if node_u.parent is None:\n            self.root = node_v\n        elif node_u is node_u.parent.left:\n            node_u.parent.left = node_v\n        elif node_u is node_u.parent.right:\n            node_u.parent.right = node_v\n        # check is node_v is None\n        if node_v:\n            node_v.parent = node_u.parent\n\n    def maximum(self, node):\n        \"\"\"\n        find the max node when node regard as a root node\n        :param node:\n        :return: max node\n        \"\"\"\n        temp_node = node\n        while temp_node.right is not None:\n            temp_node = temp_node.right\n        return temp_node\n\n    def minimum(self, node):\n        \"\"\"\n        find the minimum node when node regard as a root node\n        :param node:\n        :return: minimum node\n        \"\"\"\n        temp_node = node\n        while temp_node.left:\n            temp_node = temp_node.left\n        return temp_node\n\n    def delete(self, node):\n        # find the node position\n        node_color = node.color\n        if node.left is None:\n            temp_node = node.right\n            self.transplant(node, node.right)\n        elif node.right is None:\n            temp_node = node.left\n            self.transplant(node, node.left)\n        else:\n            # both child exits ,and find minimum child of right child\n            node_min = self.minimum(node.right)\n            node_color = node_min.color\n            temp_node = node_min.right\n            ##\n            if node_min.parent is not node:\n                self.transplant(node_min, node_min.right)\n                node_min.right = node.right\n                node_min.right.parent = node_min\n            self.transplant(node, node_min)\n            node_min.left = node.left\n            node_min.left.parent = node_min\n            node_min.color = node.color\n        # when node is black, then need to fix it with 4 cases\n        if node_color == 0:\n            self.delete_fixup(temp_node)\n\n    def delete_fixup(self, node):\n        # 4 cases\n        while node is not self.root and node.color == 0:\n            # node is not root and color is black\n            if node is node.parent.left:\n                # node is left node\n                node_brother = node.parent.right\n\n                # case 1: node's red, can not get black node\n                # set brother is black and parent is red\n                if node_brother.color == 1:\n                    node_brother.color = 0\n                    node.parent.color = 1\n                    self.left_rotate(node.parent)\n                    node_brother = node.parent.right\n\n                # case 2: brother node is black, and its children node is both black\n                if (node_brother.left is None or node_brother.left.color == 0) and (\n                    node_brother.right is None or node_brother.right.color == 0\n                ):\n                    node_brother.color = 1\n                    node = node.parent\n                else:\n                    # case 3: brother node is black, and its\n                    # left child node is red and right is black\n                    if node_brother.right is None or node_brother.right.color == 0:\n                        node_brother.color = 1\n                        node_brother.left.color = 0\n                        self.right_rotate(node_brother)\n                        node_brother = node.parent.right\n\n                    # case 4: brother node is black, and right\n                    # is red, and left is any color\n                    node_brother.color = node.parent.color\n                    node.parent.color = 0\n                    node_brother.right.color = 0\n                    self.left_rotate(node.parent)\n                    node = self.root\n            else:\n                node_brother = node.parent.left\n                if node_brother.color == 1:\n                    node_brother.color = 0\n                    node.parent.color = 1\n                    self.left_rotate(node.parent)\n                    node_brother = node.parent.right\n                if (node_brother.left is None or node_brother.left.color == 0) and (\n                    node_brother.right is None or node_brother.right.color == 0\n                ):\n                    node_brother.color = 1\n                    node = node.parent\n                else:\n                    if node_brother.left is None or node_brother.left.color == 0:\n                        node_brother.color = 1\n                        node_brother.right.color = 0\n                        self.left_rotate(node_brother)\n                        node_brother = node.parent.left\n                    node_brother.color = node.parent.color\n                    node.parent.color = 0\n                    node_brother.left.color = 0\n                    self.right_rotate(node.parent)\n                    node = self.root\n        node.color = 0\n\n    def inorder(self):\n        res = []\n        if not self.root:\n            return res\n        stack = []\n        root = self.root\n        while root or stack:\n            while root:\n                stack.append(root)\n                root = root.left\n            root = stack.pop()\n            res.append({\"val\": root.val, \"color\": root.color})\n            root = root.right\n        return res\n\n\nif __name__ == \"__main__\":\n    rb = RBTree()\n    children = [11, 2, 14, 1, 7, 15, 5, 8, 4]\n    for child in children:\n        node = RBNode(child, 1)\n        print(child)\n        rb.insert(node)\n    print(rb.inorder())\n"
  },
  {
    "path": "algorithms/data_structures/segment_tree.py",
    "content": "\"\"\"\nSegment_tree creates a segment tree with a given array and function,\nallowing queries to be done later in log(N) time\nfunction takes 2 values and returns a same type value\n\"\"\"\n\n\nclass SegmentTree:\n    def __init__(self, arr, function):\n        self.segment = [0 for x in range(3 * len(arr) + 3)]\n        self.arr = arr\n        self.fn = function\n        self.make_tree(0, 0, len(arr) - 1)\n\n    def make_tree(self, i, left, r):\n        if left == r:\n            self.segment[i] = self.arr[left]\n        elif left < r:\n            self.make_tree(2 * i + 1, left, int((left + r) / 2))\n            self.make_tree(2 * i + 2, int((left + r) / 2) + 1, r)\n            self.segment[i] = self.fn(\n                self.segment[2 * i + 1], self.segment[2 * i + 2]\n            )\n\n    def __query(self, i, low, high, left, r):\n        if left > high or r < low or low > high or left > r:\n            return None\n        if left <= low and r >= high:\n            return self.segment[i]\n        val1 = self.__query(2 * i + 1, low, int((low + high) / 2), left, r)\n        val2 = self.__query(\n            2 * i + 2, int((low + high + 2) / 2), high, left, r\n        )\n        print(low, high, \" returned \", val1, val2)\n        if val1 is not None:\n            if val2 is not None:\n                return self.fn(val1, val2)\n            return val1\n        return val2\n\n    def query(self, low, high):\n        return self.__query(0, 0, len(self.arr) - 1, low, high)\n\n\n\"\"\"\nExample -\nmytree = SegmentTree([2,4,5,3,4],max)\nmytree.query(2,4)\nmytree.query(0,3) ...\n\nmytree = SegmentTree([4,5,2,3,4,43,3],sum)\nmytree.query(1,8)\n...\n\n\"\"\"\n"
  },
  {
    "path": "algorithms/data_structures/separate_chaining_hash_table.py",
    "content": "\"\"\"\nSeparate Chaining Hash Table\n\nHash table implementation using separate chaining (linked lists) for\ncollision resolution.\n\nReference: https://en.wikipedia.org/wiki/Hash_table#Separate_chaining\n\nComplexity:\n    Time:  O(1) average for put/get/del, O(n) worst case\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass _Node:\n    \"\"\"Internal linked list node for chaining.\n\n    Args:\n        key: The key stored in this node.\n        value: The value stored in this node.\n        next_node: Reference to the next node in the chain.\n    \"\"\"\n\n    def __init__(\n        self,\n        key: object = None,\n        value: object = None,\n        next_node: _Node | None = None,\n    ) -> None:\n        self.key = key\n        self.value = value\n        self.next = next_node\n\n\nclass SeparateChainingHashTable:\n    \"\"\"Hash table using separate chaining for collision resolution.\n\n    Examples:\n        >>> table = SeparateChainingHashTable()\n        >>> table.put('hello', 'world')\n        >>> len(table)\n        1\n        >>> table.get('hello')\n        'world'\n        >>> del table['hello']\n        >>> table.get('hello') is None\n        True\n    \"\"\"\n\n    _empty = None\n\n    def __init__(self, size: int = 11) -> None:\n        \"\"\"Initialize the hash table.\n\n        Args:\n            size: Number of buckets.\n        \"\"\"\n        self.size = size\n        self._len = 0\n        self._table: list[_Node | None] = [self._empty] * size\n\n    def put(self, key: object, value: object) -> None:\n        \"\"\"Insert or update a key-value pair.\n\n        Args:\n            key: The key to insert.\n            value: The value associated with the key.\n        \"\"\"\n        hash_ = self.hash(key)\n        node_ = self._table[hash_]\n        if node_ is self._empty:\n            self._table[hash_] = _Node(key, value)\n        else:\n            while node_.next is not None:\n                if node_.key == key:\n                    node_.value = value\n                    return\n                node_ = node_.next\n            node_.next = _Node(key, value)\n        self._len += 1\n\n    def get(self, key: object) -> object | None:\n        \"\"\"Retrieve the value for a given key.\n\n        Args:\n            key: The key to look up.\n\n        Returns:\n            The associated value, or None if the key is not found.\n        \"\"\"\n        hash_ = self.hash(key)\n        node_ = self._table[hash_]\n        while node_ is not self._empty:\n            if node_.key == key:\n                return node_.value\n            node_ = node_.next\n        return None\n\n    def del_(self, key: object) -> None:\n        \"\"\"Delete a key-value pair.\n\n        Args:\n            key: The key to delete.\n        \"\"\"\n        hash_ = self.hash(key)\n        node_ = self._table[hash_]\n        previous_node = None\n        while node_ is not None:\n            if node_.key == key:\n                if previous_node is None:\n                    self._table[hash_] = node_.next\n                else:\n                    previous_node.next = node_.next\n                self._len -= 1\n            previous_node = node_\n            node_ = node_.next\n\n    def hash(self, key: object) -> int:\n        \"\"\"Compute the bucket index for a key.\n\n        Args:\n            key: The key to hash.\n\n        Returns:\n            Bucket index.\n        \"\"\"\n        return hash(key) % self.size\n\n    def __len__(self) -> int:\n        return self._len\n\n    def __getitem__(self, key: object) -> object | None:\n        return self.get(key)\n\n    def __delitem__(self, key: object) -> None:\n        return self.del_(key)\n\n    def __setitem__(self, key: object, value: object) -> None:\n        self.put(key, value)\n"
  },
  {
    "path": "algorithms/data_structures/sqrt_decomposition.py",
    "content": "\"\"\"\nSquare Root (Sqrt) Decomposition\n\nDivides an array into blocks of size √n to allow O(√n) range queries and\npoint updates — a simple alternative to segment trees for range-aggregate\nproblems.\n\nSupports:\n- **Range sum queries** in O(√n).\n- **Point updates** in O(1).\n\nReference: https://cp-algorithms.com/data_structures/sqrt_decomposition.html\n\nComplexity:\n    Build: O(n)\n    Query: O(√n)\n    Update: O(1)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\nclass SqrtDecomposition:\n    \"\"\"Square root decomposition for range sum queries.\n\n    Attributes:\n        data: The underlying array.\n        block_size: Size of each block (⌈√n⌉).\n        blocks: Precomputed block sums.\n\n    Examples:\n        >>> sd = SqrtDecomposition([1, 2, 3, 4, 5, 6, 7, 8, 9])\n        >>> sd.query(0, 8)\n        45\n        >>> sd.query(2, 5)\n        18\n        >>> sd.update(4, 10)\n        >>> sd.query(0, 8)\n        50\n    \"\"\"\n\n    def __init__(self, arr: list[int | float]) -> None:\n        \"\"\"Build the sqrt decomposition from *arr*.\n\n        Args:\n            arr: Input array of numbers.\n        \"\"\"\n        self.data = list(arr)\n        n = len(self.data)\n        self.block_size = max(1, math.isqrt(n))\n        num_blocks = (n + self.block_size - 1) // self.block_size\n        self.blocks: list[int | float] = [0] * num_blocks\n\n        for i, val in enumerate(self.data):\n            self.blocks[i // self.block_size] += val\n\n    def update(self, index: int, value: int | float) -> None:\n        \"\"\"Set ``data[index]`` to *value* and update the block sum.\n\n        Args:\n            index: Array index to update.\n            value: New value.\n\n        Raises:\n            IndexError: If *index* is out of range.\n\n        Examples:\n            >>> sd = SqrtDecomposition([1, 2, 3])\n            >>> sd.update(1, 10)\n            >>> sd.query(0, 2)\n            14\n        \"\"\"\n        if index < 0 or index >= len(self.data):\n            msg = f\"index {index} out of range for length {len(self.data)}\"\n            raise IndexError(msg)\n\n        block = index // self.block_size\n        self.blocks[block] += value - self.data[index]\n        self.data[index] = value\n\n    def query(self, left: int, right: int) -> int | float:\n        \"\"\"Return the sum of elements from *left* to *right* inclusive.\n\n        Args:\n            left: Start index (inclusive).\n            right: End index (inclusive).\n\n        Returns:\n            Sum of ``data[left..right]``.\n\n        Raises:\n            IndexError: If indices are out of range.\n\n        Examples:\n            >>> sd = SqrtDecomposition([1, 2, 3, 4, 5])\n            >>> sd.query(1, 3)\n            9\n        \"\"\"\n        if left < 0 or right >= len(self.data) or left > right:\n            msg = f\"invalid range [{left}, {right}] for length {len(self.data)}\"\n            raise IndexError(msg)\n\n        total: int | float = 0\n        block_left = left // self.block_size\n        block_right = right // self.block_size\n\n        if block_left == block_right:\n            # Same block — iterate directly\n            for i in range(left, right + 1):\n                total += self.data[i]\n        else:\n            # Partial left block\n            for i in range(left, (block_left + 1) * self.block_size):\n                total += self.data[i]\n            # Full middle blocks\n            for b in range(block_left + 1, block_right):\n                total += self.blocks[b]\n            # Partial right block\n            for i in range(block_right * self.block_size, right + 1):\n                total += self.data[i]\n\n        return total\n"
  },
  {
    "path": "algorithms/data_structures/stack.py",
    "content": "\"\"\"\nStack Abstract Data Type\n\nImplementations of the stack ADT using both a fixed-size array and a\nlinked list. Both support push, pop, peek, is_empty, len, iter, and str.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(1) for push/pop/peek (amortized for ArrayStack)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom abc import ABCMeta, abstractmethod\nfrom collections.abc import Iterator\n\n\nclass AbstractStack(metaclass=ABCMeta):\n    \"\"\"Abstract base class for stack implementations.\"\"\"\n\n    def __init__(self) -> None:\n        self._top = -1\n\n    def __len__(self) -> int:\n        return self._top + 1\n\n    def __str__(self) -> str:\n        result = \" \".join(map(str, self))\n        return \"Top-> \" + result\n\n    def is_empty(self) -> bool:\n        \"\"\"Check if the stack is empty.\n\n        Returns:\n            True if the stack has no elements.\n        \"\"\"\n        return self._top == -1\n\n    @abstractmethod\n    def __iter__(self) -> Iterator[object]:\n        pass\n\n    @abstractmethod\n    def push(self, value: object) -> None:\n        pass\n\n    @abstractmethod\n    def pop(self) -> object:\n        pass\n\n    @abstractmethod\n    def peek(self) -> object:\n        pass\n\n\nclass ArrayStack(AbstractStack):\n    \"\"\"Stack implemented with a dynamic array.\n\n    Examples:\n        >>> s = ArrayStack()\n        >>> s.push(1)\n        >>> s.pop()\n        1\n    \"\"\"\n\n    def __init__(self, size: int = 10) -> None:\n        \"\"\"Initialize with a fixed-size array.\n\n        Args:\n            size: Initial capacity of the underlying array.\n        \"\"\"\n        super().__init__()\n        self._array: list[object | None] = [None] * size\n\n    def __iter__(self) -> Iterator[object]:\n        probe = self._top\n        while True:\n            if probe == -1:\n                return\n            yield self._array[probe]\n            probe -= 1\n\n    def push(self, value: object) -> None:\n        \"\"\"Push a value onto the stack.\n\n        Args:\n            value: The value to push.\n        \"\"\"\n        self._top += 1\n        if self._top == len(self._array):\n            self._expand()\n        self._array[self._top] = value\n\n    def pop(self) -> object:\n        \"\"\"Remove and return the top element.\n\n        Returns:\n            The top element.\n\n        Raises:\n            IndexError: If the stack is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Stack is empty\")\n        value = self._array[self._top]\n        self._top -= 1\n        return value\n\n    def peek(self) -> object:\n        \"\"\"Return the top element without removing it.\n\n        Returns:\n            The top element.\n\n        Raises:\n            IndexError: If the stack is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Stack is empty\")\n        return self._array[self._top]\n\n    def _expand(self) -> None:\n        \"\"\"Double the size of the underlying array.\"\"\"\n        self._array += [None] * len(self._array)\n\n\nclass StackNode:\n    \"\"\"A single node in a linked-list-based stack.\"\"\"\n\n    def __init__(self, value: object) -> None:\n        self.value = value\n        self.next: StackNode | None = None\n\n\nclass LinkedListStack(AbstractStack):\n    \"\"\"Stack implemented with a singly linked list.\n\n    Examples:\n        >>> s = LinkedListStack()\n        >>> s.push(1)\n        >>> s.pop()\n        1\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        self.head: StackNode | None = None\n\n    def __iter__(self) -> Iterator[object]:\n        probe = self.head\n        while True:\n            if probe is None:\n                return\n            yield probe.value\n            probe = probe.next\n\n    def push(self, value: object) -> None:\n        \"\"\"Push a value onto the stack.\n\n        Args:\n            value: The value to push.\n        \"\"\"\n        node = StackNode(value)\n        node.next = self.head\n        self.head = node\n        self._top += 1\n\n    def pop(self) -> object:\n        \"\"\"Remove and return the top element.\n\n        Returns:\n            The top element.\n\n        Raises:\n            IndexError: If the stack is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Stack is empty\")\n        value = self.head.value\n        self.head = self.head.next\n        self._top -= 1\n        return value\n\n    def peek(self) -> object:\n        \"\"\"Return the top element without removing it.\n\n        Returns:\n            The top element.\n\n        Raises:\n            IndexError: If the stack is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Stack is empty\")\n        return self.head.value\n"
  },
  {
    "path": "algorithms/data_structures/trie.py",
    "content": "\"\"\"\nImplement a trie with insert, search, and startsWith methods.\n\nNote:\nYou may assume that all inputs are consist of lowercase letters a-z.\n\"\"\"\n\nimport collections\n\n\nclass TrieNode:\n    def __init__(self):\n        self.children = collections.defaultdict(TrieNode)\n        self.is_word = False\n\n\nclass Trie:\n    def __init__(self):\n        self.root = TrieNode()\n\n    def insert(self, word):\n        current = self.root\n        for letter in word:\n            current = current.children[letter]\n        current.is_word = True\n\n    def search(self, word):\n        current = self.root\n        for letter in word:\n            current = current.children.get(letter)\n            if current is None:\n                return False\n        return current.is_word\n\n    def starts_with(self, prefix):\n        current = self.root\n        for letter in prefix:\n            current = current.children.get(letter)\n            if current is None:\n                return False\n        return True\n"
  },
  {
    "path": "algorithms/data_structures/union_find.py",
    "content": "\"\"\"\nUnion-Find (Disjoint Set) Data Structure\n\nA Union-Find data structure supporting add, find (root), and unite operations.\nUses union by size and path compression for near-constant amortized time.\n\nReference: https://en.wikipedia.org/wiki/Disjoint-set_data_structure\n\nComplexity:\n    Time:  O(alpha(n)) amortized per operation (inverse Ackermann)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Union:\n    \"\"\"A Union-Find (Disjoint Set) data structure.\n\n    Supports adding elements, finding set representatives, and merging sets.\n    Uses union by size and path compression for near-constant amortized time.\n\n    Examples:\n        >>> uf = Union()\n        >>> uf.add(1); uf.add(2); uf.add(3)\n        >>> uf.unite(1, 2)\n        >>> uf.root(1) == uf.root(2)\n        True\n        >>> uf.root(1) == uf.root(3)\n        False\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.parents: dict[object, object] = {}\n        self.size: dict[object, int] = {}\n        self.count: int = 0\n\n    def add(self, element: object) -> None:\n        \"\"\"Add a new singleton set containing the given element.\n\n        Args:\n            element: The element to add.\n        \"\"\"\n        self.parents[element] = element\n        self.size[element] = 1\n        self.count += 1\n\n    def root(self, element: object) -> object:\n        \"\"\"Find the root representative of the set containing element.\n\n        Args:\n            element: The element whose root to find.\n\n        Returns:\n            The root representative of the element's set.\n        \"\"\"\n        while element != self.parents[element]:\n            self.parents[element] = self.parents[self.parents[element]]\n            element = self.parents[element]\n        return element\n\n    def unite(self, element1: object, element2: object) -> None:\n        \"\"\"Merge the sets containing the two elements.\n\n        Args:\n            element1: An element in the first set.\n            element2: An element in the second set.\n        \"\"\"\n        root1, root2 = self.root(element1), self.root(element2)\n        if root1 == root2:\n            return\n        if self.size[root1] > self.size[root2]:\n            root1, root2 = root2, root1\n        self.parents[root1] = root2\n        self.size[root2] += self.size[root1]\n        self.count -= 1\n"
  },
  {
    "path": "algorithms/data_structures/veb_tree.py",
    "content": "\"\"\"\nVan Emde Boas Tree (vEB Tree) / van Emde Boas priority queue\n\nReference: https://en.wikipedia.org/wiki/Van_Emde_Boas_tree\n\nA van Emde Boas tree is a recursive data structure for storing integers\nfrom a fixed universe [0, u - 1], where u is a power of 2.\n\nTime complexity:\n    insert / delete / successor / member : O(log log u)\n    min / max                            : O(1)\n\nSpace complexity:\n    O(u)\n\"\"\"\n\nimport math\n\n\nclass VEBTree:\n    \"\"\"\n    Van Emde Boas tree supporting fast predecessor/successor queries.\n\n    Attributes:\n        u (int): Universe size (power of 2)\n        min (int | None): Minimum element in the tree\n        max (int | None): Maximum element in the tree\n        summary (VEBTree | None): Summary tree\n        cluster (list[VEBTree] | None): Array of clusters\n    \"\"\"\n\n    def __init__(self, universe_size):\n        \"\"\"\n        Initialize a Van Emde Boas tree.\n\n        Args:\n            universe_size (int): Size of the universe; must be a power of 2 and > 0.\n\n        Raises:\n            TypeError: If universe_size is not an integer.\n            ValueError: If universe_size <= 0 or not a power of 2.\n        \"\"\"\n        if not isinstance(universe_size, int):\n            raise TypeError(\"universe_size must be an integer.\")\n        if not universe_size > 0:\n            raise ValueError(\"universe_size must be greater than 0.\")\n        if not (universe_size & (universe_size - 1)) == 0:\n            raise ValueError(\"universe_size must be a power of 2.\")\n\n        self.u = universe_size\n        self.min = None\n        self.max = None\n\n        if universe_size <= 2:\n            self.summary = None\n            self.cluster = None\n        else:\n            self.lower_sqrt = 2 ** (math.floor(math.log2(universe_size) / 2))\n            self.upper_sqrt = 2 ** (math.ceil(math.log2(universe_size) / 2))\n\n            self.summary = VEBTree(self.upper_sqrt)\n            self.cluster = [VEBTree(self.lower_sqrt) for _ in range(self.upper_sqrt)]\n\n    def _validate_key(self, x):\n        \"\"\"\n        Check if x is within the universe range.\n\n        Args:\n            x (int): Element to validate.\n\n        Raises:\n            ValueError: If x is not in the range [0, u-1].\n        \"\"\"\n        if not (0 <= x < self.u):\n            raise ValueError(f\"Key {x} out of universe range [0, {self.u - 1}]\")\n\n    def high(self, x):\n        \"\"\"\n        Return the high part (cluster index) of element x.\n\n        Args:\n            x (int): Element to split.\n\n        Returns:\n            int: Cluster index corresponding to x.\n        \"\"\"\n        return x // self.lower_sqrt\n\n    def low(self, x):\n        \"\"\"\n        Return the low part (position within cluster) of element x.\n\n        Args:\n            x (int): Element to split.\n\n        Returns:\n            int: Position within cluster corresponding to x.\n        \"\"\"\n        return x % self.lower_sqrt\n\n    def index(self, high, low):\n        \"\"\"\n        Combine high and low parts to get original element.\n\n        Args:\n            high (int): Cluster index.\n            low (int): Position within cluster.\n\n        Returns:\n            int: Original element corresponding to high and low.\n        \"\"\"\n        return high * self.lower_sqrt + low\n\n    def empty_insert(self, x):\n        \"\"\"\n        Insert x into an empty vEB tree (sets min and max).\n\n        Args:\n            x (int): Element to insert.\n        \"\"\"\n        self.min = self.max = x\n\n    def insert(self, x):\n        \"\"\"\n        Insert an element into the Van Emde Boas tree.\n\n        Args:\n            x (int): Element to insert; must be in the universe [0, u-1].\n\n        Raises:\n            ValueError: If x is outside the universe.\n        \"\"\"\n        self._validate_key(x)\n        if self.min is None:\n            self.empty_insert(x)\n            return\n\n        if x < self.min:\n            x, self.min = self.min, x\n\n        if self.u > 2:\n            high = self.high(x)\n            low = self.low(x)\n\n            if self.cluster[high].min is None:\n                self.summary.insert(high)\n                self.cluster[high].empty_insert(low)\n            else:\n                self.cluster[high].insert(low)\n\n        if x > self.max:\n            self.max = x\n\n    def member(self, x):\n        \"\"\"\n        Check whether element x exists in the tree.\n\n        Args:\n            x (int): Element to check.\n\n        Returns:\n            bool: True if x exists, False otherwise.\n\n        Raises:\n            ValueError: If x is outside the universe.\n        \"\"\"\n        self._validate_key(x)\n        if x == self.min or x == self.max:\n            return True\n        elif self.u == 2:\n            return False\n        else:\n            return self.cluster[self.high(x)].member(self.low(x))\n\n    def successor(self, x):\n        \"\"\"\n        Return the smallest element greater than x in the tree.\n\n        Args:\n            x (int): Element to find successor for.\n\n        Returns:\n            int | None: Successor of x if exists, otherwise None.\n\n        Raises:\n            ValueError: If x is outside the universe.\n        \"\"\"\n        self._validate_key(x)\n        if self.u == 2:\n            if x == 0 and self.max == 1:\n                return 1\n            return None\n\n        if self.min is not None and x < self.min:\n            return self.min\n\n        high = self.high(x)\n        low = self.low(x)\n\n        max_low = self.cluster[high].max\n\n        if max_low is not None and low < max_low:\n            offset = self.cluster[high].successor(low)\n            return self.index(high, offset)\n        else:\n            succ_cluster = self.summary.successor(high)\n            if succ_cluster is None:\n                return None\n            offset = self.cluster[succ_cluster].min\n            return self.index(succ_cluster, offset)\n\n    def delete(self, x):\n        \"\"\"\n        Remove element x from the Van Emde Boas tree.\n\n        Args:\n            x (int): Element to delete.\n\n        Raises:\n            ValueError: If x is outside the universe.\n        \"\"\"\n        self._validate_key(x)\n        if self.min == self.max:\n            self.min = self.max = None\n            return\n\n        if self.u == 2:\n            if x == 0:\n                self.min = 1\n            else:\n                self.min = 0\n            self.max = self.min\n            return\n\n        if x == self.min:\n            first_cluster = self.summary.min\n            x = self.index(first_cluster, self.cluster[first_cluster].min)\n            self.min = x\n\n        high = self.high(x)\n        low = self.low(x)\n        self.cluster[high].delete(low)\n\n        if self.cluster[high].min is None:\n            self.summary.delete(high)\n\n            if x == self.max:\n                summary_max = self.summary.max\n                if summary_max is None:\n                    self.max = self.min\n                else:\n                    self.max = self.index(summary_max, self.cluster[summary_max].max)\n        elif x == self.max:\n            self.max = self.index(high, self.cluster[high].max)\n\n    def minimum(self):\n        \"\"\"\n        Get the minimum element in the tree.\n\n        Returns:\n            int | None: Minimum element, or None if tree is empty.\n        \"\"\"\n        return self.min\n\n    def maximum(self):\n        \"\"\"\n        Get the maximum element in the tree.\n\n        Returns:\n            int | None: Maximum element, or None if tree is empty.\n        \"\"\"\n        return self.max\n"
  },
  {
    "path": "algorithms/dynamic_programming/__init__.py",
    "content": "\"\"\"\nDynamic Programming Algorithms\n\nA collection of dynamic programming algorithm implementations.\n\"\"\"\n\nfrom . import regex_matching\nfrom .bitmask import tsp\nfrom .buy_sell_stock import max_profit_naive, max_profit_optimized\nfrom .climbing_stairs import climb_stairs, climb_stairs_optimized\nfrom .coin_change import count\nfrom .combination_sum import combination_sum_bottom_up, combination_sum_topdown\nfrom .count_paths_dp import count_paths_dp, count_paths_memo, count_paths_recursive\nfrom .edit_distance import edit_distance\nfrom .egg_drop import egg_drop\nfrom .fib import fib_iter, fib_list, fib_recursive\nfrom .hosoya_triangle import hosoya, hosoya_testing\nfrom .house_robber import house_robber\nfrom .int_divide import int_divide\nfrom .job_scheduling import Job, schedule\nfrom .k_factor import find_k_factor\nfrom .knapsack import Item, get_maximum_value\nfrom .longest_common_subsequence import longest_common_subsequence\nfrom .longest_increasing import (\n    longest_increasing_subsequence,\n    longest_increasing_subsequence_optimized,\n    longest_increasing_subsequence_optimized2,\n)\nfrom .matrix_chain_order import matrix_chain_order\nfrom .max_product_subarray import max_product, subarray_with_max_product\nfrom .max_subarray import max_subarray\nfrom .min_cost_path import min_cost\nfrom .num_decodings import num_decodings, num_decodings2\nfrom .planting_trees import planting_trees\nfrom .regex_matching import is_match\nfrom .rod_cut import cut_rod\nfrom .word_break import word_break\n\n__all__ = [\n    # buy_sell_stock\n    \"max_profit_naive\",\n    \"max_profit_optimized\",\n    # climbing_stairs\n    \"climb_stairs\",\n    \"climb_stairs_optimized\",\n    # coin_change\n    \"count\",\n    # combination_sum\n    \"combination_sum_topdown\",\n    \"combination_sum_bottom_up\",\n    # edit_distance\n    \"edit_distance\",\n    # egg_drop\n    \"egg_drop\",\n    # fib\n    \"fib_recursive\",\n    \"fib_list\",\n    \"fib_iter\",\n    # hosoya_triangle\n    \"hosoya\",\n    \"hosoya_testing\",\n    # house_robber\n    \"house_robber\",\n    # int_divide\n    \"int_divide\",\n    # job_scheduling\n    \"Job\",\n    \"schedule\",\n    # k_factor\n    \"find_k_factor\",\n    # knapsack\n    \"Item\",\n    \"get_maximum_value\",\n    # longest_common_subsequence\n    \"longest_common_subsequence\",\n    # longest_increasing\n    \"longest_increasing_subsequence\",\n    \"longest_increasing_subsequence_optimized\",\n    \"longest_increasing_subsequence_optimized2\",\n    # matrix_chain_order\n    \"matrix_chain_order\",\n    # max_product_subarray\n    \"max_product\",\n    \"subarray_with_max_product\",\n    # max_subarray\n    \"max_subarray\",\n    # min_cost_path\n    \"min_cost\",\n    # num_decodings\n    \"num_decodings\",\n    \"num_decodings2\",\n    # planting_trees\n    \"planting_trees\",\n    # regex_matching\n    \"regex_matching\",\n    \"is_match\",\n    # rod_cut\n    \"cut_rod\",\n    # word_break\n    \"word_break\",\n    # bitmask\n    \"tsp\",\n    # count_paths_dp\n    \"count_paths_dp\",\n    \"count_paths_memo\",\n    \"count_paths_recursive\",\n]\n"
  },
  {
    "path": "algorithms/dynamic_programming/bitmask.py",
    "content": "\"\"\"Bitmask dynamic programming — Travelling Salesman Problem (TSP).\n\nUses DP with bitmask to find the minimum-cost Hamiltonian cycle in a\nweighted graph. The state (visited_mask, current_city) encodes which\ncities have been visited and where we are now.\n\nTime: O(2^n * n^2).  Space: O(2^n * n).\n\nInspired by PR #855 (AmandaStromdahl).\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef tsp(dist: list[list[float]]) -> float:\n    \"\"\"Return the minimum cost of a Hamiltonian cycle.\n\n    *dist* is an n x n distance matrix.\n\n    >>> tsp([[0, 10, 15, 20],\n    ...      [10, 0, 35, 25],\n    ...      [15, 35, 0, 30],\n    ...      [20, 25, 30, 0]])\n    80\n    \"\"\"\n    n = len(dist)\n    full_mask = (1 << n) - 1\n    dp: dict[tuple[int, int], float] = {}\n\n    def solve(mask: int, pos: int) -> float:\n        if mask == full_mask:\n            return dist[pos][0]\n        key = (mask, pos)\n        if key in dp:\n            return dp[key]\n        ans = math.inf\n        for city in range(n):\n            if not (mask & (1 << city)):\n                ans = min(ans, dist[pos][city] + solve(mask | (1 << city), city))\n        dp[key] = ans\n        return ans\n\n    return solve(1, 0)\n"
  },
  {
    "path": "algorithms/dynamic_programming/buy_sell_stock.py",
    "content": "\"\"\"\nBest Time to Buy and Sell Stock\n\nGiven an array of stock prices, find the maximum profit from a single\nbuy-sell transaction (buy before sell).\n\nReference: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/\n\nComplexity:\n    max_profit_naive:\n        Time:  O(n^2)\n        Space: O(1)\n    max_profit_optimized:\n        Time:  O(n)\n        Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_profit_naive(prices: list[int]) -> int:\n    \"\"\"Find maximum profit by checking all pairs of buy/sell days.\n\n    Args:\n        prices: List of stock prices per day.\n\n    Returns:\n        Maximum achievable profit (0 if no profitable trade exists).\n\n    Examples:\n        >>> max_profit_naive([7, 1, 5, 3, 6, 4])\n        5\n        >>> max_profit_naive([7, 6, 4, 3, 1])\n        0\n    \"\"\"\n    max_so_far = 0\n    for i in range(0, len(prices) - 1):\n        for j in range(i + 1, len(prices)):\n            max_so_far = max(max_so_far, prices[j] - prices[i])\n    return max_so_far\n\n\ndef max_profit_optimized(prices: list[int]) -> int:\n    \"\"\"Find maximum profit using Kadane-style single pass.\n\n    Args:\n        prices: List of stock prices per day.\n\n    Returns:\n        Maximum achievable profit (0 if no profitable trade exists).\n\n    Examples:\n        >>> max_profit_optimized([7, 1, 5, 3, 6, 4])\n        5\n        >>> max_profit_optimized([7, 6, 4, 3, 1])\n        0\n    \"\"\"\n    cur_max, max_so_far = 0, 0\n    for i in range(1, len(prices)):\n        cur_max = max(0, cur_max + prices[i] - prices[i - 1])\n        max_so_far = max(max_so_far, cur_max)\n    return max_so_far\n"
  },
  {
    "path": "algorithms/dynamic_programming/climbing_stairs.py",
    "content": "\"\"\"\nClimbing Stairs\n\nCount the number of distinct ways to climb a staircase of n steps,\nwhere each move is either 1 or 2 steps.\n\nReference: https://leetcode.com/problems/climbing-stairs/\n\nComplexity:\n    climb_stairs:\n        Time:  O(n)\n        Space: O(n)\n    climb_stairs_optimized:\n        Time:  O(n)\n        Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef climb_stairs(steps: int) -> int:\n    \"\"\"Count distinct ways to climb n steps using a list-based DP approach.\n\n    Args:\n        steps: Number of steps in the staircase (positive integer).\n\n    Returns:\n        Number of distinct ways to reach the top.\n\n    Examples:\n        >>> climb_stairs(2)\n        2\n        >>> climb_stairs(10)\n        89\n    \"\"\"\n    arr = [1, 1]\n    for _ in range(1, steps):\n        arr.append(arr[-1] + arr[-2])\n    return arr[-1]\n\n\ndef climb_stairs_optimized(steps: int) -> int:\n    \"\"\"Count distinct ways to climb n steps using constant space.\n\n    Args:\n        steps: Number of steps in the staircase (positive integer).\n\n    Returns:\n        Number of distinct ways to reach the top.\n\n    Examples:\n        >>> climb_stairs_optimized(2)\n        2\n        >>> climb_stairs_optimized(10)\n        89\n    \"\"\"\n    a_steps = b_steps = 1\n    for _ in range(steps):\n        a_steps, b_steps = b_steps, a_steps + b_steps\n    return a_steps\n"
  },
  {
    "path": "algorithms/dynamic_programming/coin_change.py",
    "content": "\"\"\"\nCoin Change (Number of Ways)\n\nGiven a value and a set of coin denominations, count how many distinct\ncombinations of coins sum to the given value.\n\nReference: https://leetcode.com/problems/coin-change-ii/\n\nComplexity:\n    Time:  O(n * m)  where n is the value and m is the number of coins\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count(coins: list[int], value: int) -> int:\n    \"\"\"Find the number of coin combinations that add up to value.\n\n    Args:\n        coins: List of coin denominations.\n        value: Target sum.\n\n    Returns:\n        Number of distinct combinations that sum to value.\n\n    Examples:\n        >>> count([1, 2, 3], 4)\n        4\n        >>> count([2, 5, 3, 6], 10)\n        5\n    \"\"\"\n    dp_array = [1] + [0] * value\n\n    for coin in coins:\n        for i in range(coin, value + 1):\n            dp_array[i] += dp_array[i - coin]\n\n    return dp_array[value]\n"
  },
  {
    "path": "algorithms/dynamic_programming/combination_sum.py",
    "content": "\"\"\"\nCombination Sum IV\n\nGiven an array of distinct positive integers and a target, find the number\nof possible combinations (order matters) that add up to the target.\n\nReference: https://leetcode.com/problems/combination-sum-iv/\n\nComplexity:\n    combination_sum_topdown:\n        Time:  O(target * n)\n        Space: O(target)\n    combination_sum_bottom_up:\n        Time:  O(target * n)\n        Space: O(target)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _helper_topdown(nums: list[int], target: int, dp: list[int]) -> int:\n    \"\"\"Recursive helper that fills the dp table top-down.\n\n    Args:\n        nums: Positive integer array without duplicates.\n        target: Remaining target value.\n        dp: Memoisation table.\n\n    Returns:\n        Number of combinations that sum to target.\n    \"\"\"\n    if dp[target] != -1:\n        return dp[target]\n    result = 0\n    for num in nums:\n        if target >= num:\n            result += _helper_topdown(nums, target - num, dp)\n    dp[target] = result\n    return result\n\n\ndef combination_sum_topdown(nums: list[int], target: int) -> int:\n    \"\"\"Find number of combinations that add up to target (top-down DP).\n\n    Args:\n        nums: Positive integer array without duplicates.\n        target: Target sum.\n\n    Returns:\n        Number of ordered combinations that sum to target.\n\n    Examples:\n        >>> combination_sum_topdown([1, 2, 3], 4)\n        7\n    \"\"\"\n    dp = [-1] * (target + 1)\n    dp[0] = 1\n    return _helper_topdown(nums, target, dp)\n\n\ndef combination_sum_bottom_up(nums: list[int], target: int) -> int:\n    \"\"\"Find number of combinations that add up to target (bottom-up DP).\n\n    Args:\n        nums: Positive integer array without duplicates.\n        target: Target sum.\n\n    Returns:\n        Number of ordered combinations that sum to target.\n\n    Examples:\n        >>> combination_sum_bottom_up([1, 2, 3], 4)\n        7\n    \"\"\"\n    combs = [0] * (target + 1)\n    combs[0] = 1\n    for i in range(0, len(combs)):\n        for num in nums:\n            if i - num >= 0:\n                combs[i] += combs[i - num]\n    return combs[target]\n"
  },
  {
    "path": "algorithms/dynamic_programming/count_paths_dp.py",
    "content": "\"\"\"Count paths in a grid — recursive, memoized, and bottom-up DP.\n\nCount the number of unique paths from the top-left to the bottom-right\nof an m x n grid, moving only right or down.\n\nInspired by PR #857 (c-cret).\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom functools import cache\n\n\ndef count_paths_recursive(m: int, n: int) -> int:\n    \"\"\"Naive recursive solution — O(2^(m+n)).\"\"\"\n    if m == 1 or n == 1:\n        return 1\n    return count_paths_recursive(m - 1, n) + count_paths_recursive(m, n - 1)\n\n\ndef count_paths_memo(m: int, n: int) -> int:\n    \"\"\"Top-down DP with memoization — O(m*n).\"\"\"\n\n    @cache\n    def helper(i: int, j: int) -> int:\n        if i == 1 or j == 1:\n            return 1\n        return helper(i - 1, j) + helper(i, j - 1)\n\n    return helper(m, n)\n\n\ndef count_paths_dp(m: int, n: int) -> int:\n    \"\"\"Bottom-up DP — O(m*n) time, O(n) space.\n\n    >>> count_paths_dp(3, 7)\n    28\n    >>> count_paths_dp(3, 3)\n    6\n    \"\"\"\n    row = [1] * n\n    for _ in range(1, m):\n        for j in range(1, n):\n            row[j] += row[j - 1]\n    return row[n - 1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/edit_distance.py",
    "content": "\"\"\"\nEdit Distance (Levenshtein Distance)\n\nFind the minimum number of insertions, deletions, and substitutions\nrequired to transform one word into another.\n\nReference: https://en.wikipedia.org/wiki/Levenshtein_distance\n\nComplexity:\n    Time:  O(m * n)  where m, n are the lengths of the two words\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef edit_distance(word_a: str, word_b: str) -> int:\n    \"\"\"Compute the edit distance between two words.\n\n    Args:\n        word_a: First word.\n        word_b: Second word.\n\n    Returns:\n        Minimum number of single-character edits to transform word_a into word_b.\n\n    Examples:\n        >>> edit_distance('food', 'money')\n        4\n        >>> edit_distance('horse', 'ros')\n        3\n    \"\"\"\n    length_a, length_b = len(word_a) + 1, len(word_b) + 1\n\n    edit = [[0 for _ in range(length_b)] for _ in range(length_a)]\n\n    for i in range(1, length_a):\n        edit[i][0] = i\n\n    for j in range(1, length_b):\n        edit[0][j] = j\n\n    for i in range(1, length_a):\n        for j in range(1, length_b):\n            cost = 0 if word_a[i - 1] == word_b[j - 1] else 1\n            edit[i][j] = min(\n                edit[i - 1][j] + 1,\n                edit[i][j - 1] + 1,\n                edit[i - 1][j - 1] + cost,\n            )\n\n    return edit[-1][-1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/egg_drop.py",
    "content": "\"\"\"\nEgg Drop Problem\n\nGiven K eggs and a building with N floors, determine the minimum number\nof moves needed to find the critical floor F in the worst case.\n\nReference: https://en.wikipedia.org/wiki/Dynamic_programming#Egg_dropping_puzzle\n\nComplexity:\n    Time:  O(n * k^2)\n    Space: O(n * k)\n\"\"\"\n\nfrom __future__ import annotations\n\n_INT_MAX = 32767\n\n\ndef egg_drop(n: int, k: int) -> int:\n    \"\"\"Find the minimum number of trials to identify the critical floor.\n\n    Args:\n        n: Number of eggs.\n        k: Number of floors.\n\n    Returns:\n        Minimum number of trials in the worst case.\n\n    Examples:\n        >>> egg_drop(1, 2)\n        2\n        >>> egg_drop(2, 6)\n        3\n    \"\"\"\n    egg_floor = [[0 for _ in range(k + 1)] for _ in range(n + 1)]\n\n    for i in range(1, n + 1):\n        egg_floor[i][1] = 1\n        egg_floor[i][0] = 0\n\n    for j in range(1, k + 1):\n        egg_floor[1][j] = j\n\n    for i in range(2, n + 1):\n        for j in range(2, k + 1):\n            egg_floor[i][j] = _INT_MAX\n            for x in range(1, j + 1):\n                res = 1 + max(egg_floor[i - 1][x - 1], egg_floor[i][j - x])\n                if res < egg_floor[i][j]:\n                    egg_floor[i][j] = res\n\n    return egg_floor[n][k]\n"
  },
  {
    "path": "algorithms/dynamic_programming/fib.py",
    "content": "\"\"\"\nFibonacci Number\n\nCompute the n-th Fibonacci number using three different approaches:\nrecursive, list-based DP, and iterative.\n\nReference: https://en.wikipedia.org/wiki/Fibonacci_number\n\nComplexity:\n    fib_recursive:\n        Time:  O(2^n)\n        Space: O(n)  (call stack)\n    fib_list:\n        Time:  O(n)\n        Space: O(n)\n    fib_iter:\n        Time:  O(n)\n        Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef fib_recursive(n: int) -> int:\n    \"\"\"Compute the n-th Fibonacci number recursively.\n\n    Args:\n        n: Non-negative integer index into the Fibonacci sequence.\n\n    Returns:\n        The n-th Fibonacci number.\n\n    Examples:\n        >>> fib_recursive(10)\n        55\n    \"\"\"\n    assert n >= 0, \"n must be a positive integer\"\n\n    if n <= 1:\n        return n\n    return fib_recursive(n - 1) + fib_recursive(n - 2)\n\n\ndef fib_list(n: int) -> int:\n    \"\"\"Compute the n-th Fibonacci number using a list-based DP table.\n\n    Args:\n        n: Non-negative integer index into the Fibonacci sequence.\n\n    Returns:\n        The n-th Fibonacci number.\n\n    Examples:\n        >>> fib_list(10)\n        55\n    \"\"\"\n    assert n >= 0, \"n must be a positive integer\"\n\n    list_results = [0, 1]\n    for i in range(2, n + 1):\n        list_results.append(list_results[i - 1] + list_results[i - 2])\n    return list_results[n]\n\n\ndef fib_iter(n: int) -> int:\n    \"\"\"Compute the n-th Fibonacci number iteratively with constant space.\n\n    Args:\n        n: Non-negative integer index into the Fibonacci sequence.\n\n    Returns:\n        The n-th Fibonacci number.\n\n    Examples:\n        >>> fib_iter(10)\n        55\n    \"\"\"\n    assert n >= 0, \"n must be positive integer\"\n\n    fib_1 = 0\n    fib_2 = 1\n    res = 0\n    if n <= 1:\n        return n\n    for _ in range(n - 1):\n        res = fib_1 + fib_2\n        fib_1 = fib_2\n        fib_2 = res\n    return res\n"
  },
  {
    "path": "algorithms/dynamic_programming/hosoya_triangle.py",
    "content": "\"\"\"\nHosoya Triangle\n\nThe Hosoya triangle (originally Fibonacci triangle) is a triangular arrangement\nof numbers where each entry is the sum of two entries above it.\n\nReference: https://en.wikipedia.org/wiki/Hosoya%27s_triangle\n\nComplexity:\n    Time:  O(n^3)  (naive recursive per entry)\n    Space: O(n)    (call stack depth)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef hosoya(height: int, width: int) -> int:\n    \"\"\"Compute a single entry in the Hosoya triangle.\n\n    Args:\n        height: Row index (0-based).\n        width: Column index (0-based).\n\n    Returns:\n        The value at position (height, width) in the Hosoya triangle.\n\n    Examples:\n        >>> hosoya(4, 2)\n        4\n    \"\"\"\n    if (width == 0) and (height in (0, 1)):\n        return 1\n    if (width == 1) and (height in (1, 2)):\n        return 1\n    if height > width:\n        return hosoya(height - 1, width) + hosoya(height - 2, width)\n    if width == height:\n        return hosoya(height - 1, width - 1) + hosoya(height - 2, width - 2)\n    return 0\n\n\ndef hosoya_testing(height: int) -> list[int]:\n    \"\"\"Generate a flat list of all Hosoya triangle values up to given height.\n\n    Args:\n        height: Number of rows to generate.\n\n    Returns:\n        Flat list of triangle values row by row.\n\n    Examples:\n        >>> hosoya_testing(1)\n        [1]\n    \"\"\"\n    res = []\n    for i in range(height):\n        for j in range(i + 1):\n            res.append(hosoya(i, j))\n    return res\n"
  },
  {
    "path": "algorithms/dynamic_programming/house_robber.py",
    "content": "\"\"\"\nHouse Robber\n\nDetermine the maximum amount of money that can be robbed from a row of\nhouses without robbing two adjacent houses.\n\nReference: https://leetcode.com/problems/house-robber/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef house_robber(houses: list[int]) -> int:\n    \"\"\"Compute the maximum robbery amount without hitting adjacent houses.\n\n    Args:\n        houses: List of non-negative integers representing money in each house.\n\n    Returns:\n        Maximum amount that can be robbed.\n\n    Examples:\n        >>> house_robber([1, 2, 16, 3, 15, 3, 12, 1])\n        44\n    \"\"\"\n    last, now = 0, 0\n    for house in houses:\n        last, now = now, max(last + house, now)\n    return now\n"
  },
  {
    "path": "algorithms/dynamic_programming/int_divide.py",
    "content": "\"\"\"\nInteger Partition\n\nCount the number of ways a positive integer can be represented as a sum\nof positive integers (order does not matter).\n\nReference: https://en.wikipedia.org/wiki/Partition_(number_theory)\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef int_divide(decompose: int) -> int:\n    \"\"\"Count the number of partitions of a positive integer.\n\n    Args:\n        decompose: The positive integer to partition.\n\n    Returns:\n        Number of distinct partitions.\n\n    Examples:\n        >>> int_divide(4)\n        5\n        >>> int_divide(7)\n        15\n    \"\"\"\n    arr = [[0 for i in range(decompose + 1)] for j in range(decompose + 1)]\n    arr[1][1] = 1\n    for i in range(1, decompose + 1):\n        for j in range(1, decompose + 1):\n            if i < j:\n                arr[i][j] = arr[i][i]\n            elif i == j:\n                arr[i][j] = 1 + arr[i][j - 1]\n            else:\n                arr[i][j] = arr[i][j - 1] + arr[i - j][j]\n    return arr[decompose][decompose]\n"
  },
  {
    "path": "algorithms/dynamic_programming/job_scheduling.py",
    "content": "\"\"\"\nWeighted Job Scheduling\n\nGiven a set of jobs with start times, finish times, and profits, find\nthe maximum profit subset such that no two jobs overlap.\n\nReference: https://en.wikipedia.org/wiki/Job-shop_scheduling\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Job:\n    \"\"\"Represents a job with start time, finish time, and profit.\"\"\"\n\n    def __init__(self, start: int, finish: int, profit: int) -> None:\n        self.start = start\n        self.finish = finish\n        self.profit = profit\n\n\ndef _binary_search(job: list[Job], start_index: int) -> int:\n    \"\"\"Find the latest non-conflicting job before start_index.\n\n    Args:\n        job: List of jobs sorted by finish time.\n        start_index: Index of the current job.\n\n    Returns:\n        Index of the latest compatible job, or -1 if none exists.\n    \"\"\"\n    left = 0\n    right = start_index - 1\n\n    while left <= right:\n        mid = (left + right) // 2\n        if job[mid].finish <= job[start_index].start:\n            if job[mid + 1].finish <= job[start_index].start:\n                left = mid + 1\n            else:\n                return mid\n        else:\n            right = mid - 1\n    return -1\n\n\ndef schedule(job: list[Job]) -> int:\n    \"\"\"Find the maximum profit from non-overlapping jobs.\n\n    Args:\n        job: List of Job objects.\n\n    Returns:\n        Maximum achievable profit.\n\n    Examples:\n        >>> schedule([Job(1, 3, 2), Job(2, 3, 4)])\n        4\n    \"\"\"\n    job = sorted(job, key=lambda j: j.finish)\n\n    length = len(job)\n    table = [0 for _ in range(length)]\n\n    table[0] = job[0].profit\n\n    for i in range(1, length):\n        incl_prof = job[i].profit\n        pos = _binary_search(job, i)\n        if pos != -1:\n            incl_prof += table[pos]\n\n        table[i] = max(incl_prof, table[i - 1])\n\n    return table[length - 1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/k_factor.py",
    "content": "\"\"\"\nK-Factor of a String\n\nThe K factor of a string is the number of times 'abba' appears as a\nsubstring. Given a length and a k_factor, count the number of strings of\nthat length whose K factor equals k_factor.\n\nReference: https://en.wikipedia.org/wiki/Dynamic_programming\n\nComplexity:\n    Time:  O(length^2)\n    Space: O(length^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_k_factor(length: int, k_factor: int) -> int:\n    \"\"\"Count strings of given length with exactly k_factor 'abba' substrings.\n\n    Args:\n        length: Length of the strings to consider.\n        k_factor: Required number of 'abba' substrings.\n\n    Returns:\n        Number of strings satisfying the K-factor constraint.\n\n    Examples:\n        >>> find_k_factor(4, 1)\n        1\n        >>> find_k_factor(7, 1)\n        70302\n    \"\"\"\n    mat = [\n        [[0 for i in range(4)] for j in range((length - 1) // 3 + 2)]\n        for k in range(length + 1)\n    ]\n    if 3 * k_factor + 1 > length:\n        return 0\n\n    mat[1][0][0] = 1\n    mat[1][0][1] = 0\n    mat[1][0][2] = 0\n    mat[1][0][3] = 25\n\n    for i in range(2, length + 1):\n        for j in range((length - 1) // 3 + 2):\n            if j == 0:\n                mat[i][j][0] = mat[i - 1][j][0] + mat[i - 1][j][1] + mat[i - 1][j][3]\n                mat[i][j][1] = mat[i - 1][j][0]\n                mat[i][j][2] = mat[i - 1][j][1]\n                mat[i][j][3] = (\n                    mat[i - 1][j][0] * 24\n                    + mat[i - 1][j][1] * 24\n                    + mat[i - 1][j][2] * 25\n                    + mat[i - 1][j][3] * 25\n                )\n\n            elif 3 * j + 1 < i:\n                mat[i][j][0] = (\n                    mat[i - 1][j][0]\n                    + mat[i - 1][j][1]\n                    + mat[i - 1][j][3]\n                    + mat[i - 1][j - 1][2]\n                )\n                mat[i][j][1] = mat[i - 1][j][0]\n                mat[i][j][2] = mat[i - 1][j][1]\n                mat[i][j][3] = (\n                    mat[i - 1][j][0] * 24\n                    + mat[i - 1][j][1] * 24\n                    + mat[i - 1][j][2] * 25\n                    + mat[i - 1][j][3] * 25\n                )\n\n            elif 3 * j + 1 == i:\n                mat[i][j][0] = 1\n                mat[i][j][1] = 0\n                mat[i][j][2] = 0\n                mat[i][j][3] = 0\n\n            else:\n                mat[i][j][0] = 0\n                mat[i][j][1] = 0\n                mat[i][j][2] = 0\n                mat[i][j][3] = 0\n\n    return sum(mat[length][k_factor])\n"
  },
  {
    "path": "algorithms/dynamic_programming/knapsack.py",
    "content": "\"\"\"\n0/1 Knapsack Problem\n\nGiven items with values and weights, and a knapsack capacity, find the\nmaximum total value that fits in the knapsack.\n\nReference: https://en.wikipedia.org/wiki/Knapsack_problem\n\nComplexity:\n    Time:  O(n * m)  where n is the number of items and m is the capacity\n    Space: O(m)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Item:\n    \"\"\"Represents an item with a value and weight.\"\"\"\n\n    def __init__(self, value: int, weight: int) -> None:\n        self.value = value\n        self.weight = weight\n\n\ndef get_maximum_value(items: list[Item], capacity: int) -> int:\n    \"\"\"Compute the maximum value achievable within the knapsack capacity.\n\n    Args:\n        items: List of Item objects with value and weight attributes.\n        capacity: Maximum weight the knapsack can hold.\n\n    Returns:\n        Maximum total value that fits in the knapsack.\n\n    Examples:\n        >>> get_maximum_value([Item(60, 5), Item(50, 3), Item(70, 4), Item(30, 2)], 5)\n        80\n    \"\"\"\n    dp = [0] * (capacity + 1)\n    for item in items:\n        for cur_weight in reversed(range(item.weight, capacity + 1)):\n            dp[cur_weight] = max(\n                dp[cur_weight], item.value + dp[cur_weight - item.weight]\n            )\n    return dp[capacity]\n"
  },
  {
    "path": "algorithms/dynamic_programming/longest_common_subsequence.py",
    "content": "\"\"\"\nLongest Common Subsequence\n\nFind the length of the longest subsequence common to two strings.\n\nReference: https://en.wikipedia.org/wiki/Longest_common_subsequence\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef longest_common_subsequence(s_1: str, s_2: str) -> int:\n    \"\"\"Compute the length of the longest common subsequence of two strings.\n\n    Args:\n        s_1: First string.\n        s_2: Second string.\n\n    Returns:\n        Length of the longest common subsequence.\n\n    Examples:\n        >>> longest_common_subsequence('abcdgh', 'aedfhr')\n        3\n    \"\"\"\n    m = len(s_1)\n    n = len(s_2)\n\n    mat = [[0] * (n + 1) for i in range(m + 1)]\n\n    for i in range(m + 1):\n        for j in range(n + 1):\n            if i == 0 or j == 0:\n                mat[i][j] = 0\n            elif s_1[i - 1] == s_2[j - 1]:\n                mat[i][j] = mat[i - 1][j - 1] + 1\n            else:\n                mat[i][j] = max(mat[i - 1][j], mat[i][j - 1])\n\n    return mat[m][n]\n"
  },
  {
    "path": "algorithms/dynamic_programming/longest_increasing.py",
    "content": "\"\"\"\nLongest Increasing Subsequence\n\nFind the length of the longest strictly increasing subsequence in an array.\n\nReference: https://en.wikipedia.org/wiki/Longest_increasing_subsequence\n\nComplexity:\n    longest_increasing_subsequence:\n        Time:  O(n^2)\n        Space: O(n)\n    longest_increasing_subsequence_optimized:\n        Time:  O(n * log(x))  where x is the max element\n        Space: O(x)\n    longest_increasing_subsequence_optimized2:\n        Time:  O(n * log(n))\n        Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef longest_increasing_subsequence(sequence: list[int]) -> int:\n    \"\"\"Find length of the longest increasing subsequence using O(n^2) DP.\n\n    Args:\n        sequence: List of integers.\n\n    Returns:\n        Length of the longest strictly increasing subsequence.\n\n    Examples:\n        >>> longest_increasing_subsequence([10, 9, 2, 5, 3, 7, 101, 18])\n        4\n    \"\"\"\n    length = len(sequence)\n    counts = [1 for _ in range(length)]\n    for i in range(1, length):\n        for j in range(0, i):\n            if sequence[i] > sequence[j]:\n                counts[i] = max(counts[i], counts[j] + 1)\n    return max(counts)\n\n\ndef longest_increasing_subsequence_optimized(sequence: list[int]) -> int:\n    \"\"\"Find length of LIS using a segment tree for O(n*log(x)) time.\n\n    Args:\n        sequence: List of integers.\n\n    Returns:\n        Length of the longest strictly increasing subsequence.\n\n    Examples:\n        >>> longest_increasing_subsequence_optimized([10, 9, 2, 5, 3, 7, 101, 18])\n        4\n    \"\"\"\n    max_val = max(sequence)\n    tree = [0] * (max_val << 2)\n\n    def _update(pos: int, left: int, right: int, target: int, vertex: int) -> None:\n        if left == right:\n            tree[pos] = vertex\n            return\n        mid = (left + right) >> 1\n        if target <= mid:\n            _update(pos << 1, left, mid, target, vertex)\n        else:\n            _update((pos << 1) | 1, mid + 1, right, target, vertex)\n        tree[pos] = max(tree[pos << 1], tree[(pos << 1) | 1])\n\n    def _get_max(pos: int, left: int, right: int, start: int, end: int) -> int:\n        if left > end or right < start:\n            return 0\n        if left >= start and right <= end:\n            return tree[pos]\n        mid = (left + right) >> 1\n        return max(\n            _get_max(pos << 1, left, mid, start, end),\n            _get_max((pos << 1) | 1, mid + 1, right, start, end),\n        )\n\n    ans = 0\n    for element in sequence:\n        cur = _get_max(1, 0, max_val, 0, element - 1) + 1\n        ans = max(ans, cur)\n        _update(1, 0, max_val, element, cur)\n    return ans\n\n\ndef longest_increasing_subsequence_optimized2(sequence: list[int]) -> int:\n    \"\"\"Find length of LIS using coordinate-compressed segment tree for O(n*log(n)).\n\n    Args:\n        sequence: List of integers.\n\n    Returns:\n        Length of the longest strictly increasing subsequence.\n\n    Examples:\n        >>> longest_increasing_subsequence_optimized2([10, 9, 2, 5, 3, 7, 101, 18])\n        4\n    \"\"\"\n    length = len(sequence)\n    tree = [0] * (length << 2)\n    sorted_seq = sorted((x, -i) for i, x in enumerate(sequence))\n\n    def _update(pos: int, left: int, right: int, target: int, vertex: int) -> None:\n        if left == right:\n            tree[pos] = vertex\n            return\n        mid = (left + right) >> 1\n        if target <= mid:\n            _update(pos << 1, left, mid, target, vertex)\n        else:\n            _update((pos << 1) | 1, mid + 1, right, target, vertex)\n        tree[pos] = max(tree[pos << 1], tree[(pos << 1) | 1])\n\n    def _get_max(pos: int, left: int, right: int, start: int, end: int) -> int:\n        if left > end or right < start:\n            return 0\n        if left >= start and right <= end:\n            return tree[pos]\n        mid = (left + right) >> 1\n        return max(\n            _get_max(pos << 1, left, mid, start, end),\n            _get_max((pos << 1) | 1, mid + 1, right, start, end),\n        )\n\n    ans = 0\n    for tup in sorted_seq:\n        i = -tup[1]\n        cur = _get_max(1, 0, length - 1, 0, i - 1) + 1\n        ans = max(ans, cur)\n        _update(1, 0, length - 1, i, cur)\n    return ans\n"
  },
  {
    "path": "algorithms/dynamic_programming/matrix_chain_order.py",
    "content": "\"\"\"\nMatrix Chain Multiplication\n\nFind the optimal parenthesization of a chain of matrices to minimize\nthe total number of scalar multiplications.\n\nReference: https://en.wikipedia.org/wiki/Matrix_chain_multiplication\n\nComplexity:\n    Time:  O(n^3)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n_INF = float(\"inf\")\n\n\ndef matrix_chain_order(array: list[int]) -> tuple[list[list[int]], list[list[int]]]:\n    \"\"\"Compute minimum multiplication cost and optimal split positions.\n\n    Args:\n        array: List of matrix dimensions where matrix i has dimensions\n               array[i-1] x array[i].\n\n    Returns:\n        A tuple of (cost_matrix, split_matrix) where cost_matrix[i][j]\n        holds the minimum cost and split_matrix[i][j] holds the optimal\n        split point.\n\n    Examples:\n        >>> m, s = matrix_chain_order([30, 35, 15, 5, 10, 20, 25])\n        >>> m[1][6]\n        15125\n    \"\"\"\n    n = len(array)\n    matrix = [[0 for x in range(n)] for x in range(n)]\n    sol = [[0 for x in range(n)] for x in range(n)]\n    for chain_length in range(2, n):\n        for a in range(1, n - chain_length + 1):\n            b = a + chain_length - 1\n\n            matrix[a][b] = _INF\n            for c in range(a, b):\n                cost = (\n                    matrix[a][c] + matrix[c + 1][b] + array[a - 1] * array[c] * array[b]\n                )\n                if cost < matrix[a][b]:\n                    matrix[a][b] = cost\n                    sol[a][b] = c\n    return matrix, sol\n"
  },
  {
    "path": "algorithms/dynamic_programming/max_product_subarray.py",
    "content": "\"\"\"\nMaximum Product Subarray\n\nFind the contiguous subarray within an array that has the largest product.\n\nReference: https://leetcode.com/problems/maximum-product-subarray/\n\nComplexity:\n    max_product:\n        Time:  O(n)\n        Space: O(1)\n    subarray_with_max_product:\n        Time:  O(n)\n        Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom functools import reduce\n\n\ndef max_product(nums: list[int]) -> int:\n    \"\"\"Find the maximum product of a contiguous subarray.\n\n    Args:\n        nums: List of integers (containing at least one number).\n\n    Returns:\n        Largest product among all contiguous subarrays.\n\n    Examples:\n        >>> max_product([2, 3, -2, 4])\n        6\n    \"\"\"\n    lmin = lmax = gmax = nums[0]\n    for num in nums[1:]:\n        t_1 = num * lmax\n        t_2 = num * lmin\n        lmax = max(max(t_1, t_2), num)\n        lmin = min(min(t_1, t_2), num)\n        gmax = max(gmax, lmax)\n    return gmax\n\n\ndef subarray_with_max_product(arr: list[int]) -> tuple[int, list[int]]:\n    \"\"\"Find the maximum product subarray and return the product and subarray.\n\n    Args:\n        arr: List of positive or negative integers.\n\n    Returns:\n        A tuple of (max_product, subarray) where subarray is the contiguous\n        slice that achieves the maximum product.\n\n    Examples:\n        >>> subarray_with_max_product([-2, -3, 6, 0, -7, -5])\n        (36, [-2, -3, 6])\n    \"\"\"\n    length = len(arr)\n    product_so_far = max_product_end = 1\n    max_start_i = 0\n    so_far_start_i = so_far_end_i = 0\n    all_negative_flag = True\n\n    for i in range(length):\n        max_product_end *= arr[i]\n        if arr[i] > 0:\n            all_negative_flag = False\n\n        if max_product_end <= 0:\n            max_product_end = arr[i]\n            max_start_i = i\n\n        if product_so_far <= max_product_end:\n            product_so_far = max_product_end\n            so_far_end_i = i\n            so_far_start_i = max_start_i\n\n    if all_negative_flag:\n        product = reduce(lambda x, y: x * y, arr)\n        return product, arr\n\n    return product_so_far, arr[so_far_start_i : so_far_end_i + 1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/max_subarray.py",
    "content": "\"\"\"\nMaximum Subarray (Kadane's Algorithm)\n\nFind the contiguous subarray with the largest sum.\n\nReference: https://en.wikipedia.org/wiki/Maximum_subarray_problem\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_subarray(array: list[int]) -> int:\n    \"\"\"Find the maximum sum of a contiguous subarray using Kadane's algorithm.\n\n    Args:\n        array: List of integers (containing at least one number).\n\n    Returns:\n        Largest sum among all contiguous subarrays.\n\n    Examples:\n        >>> max_subarray([1, 2, -3, 4, 5, -7, 23])\n        25\n    \"\"\"\n    max_so_far = max_now = array[0]\n    for i in range(1, len(array)):\n        max_now = max(array[i], max_now + array[i])\n        max_so_far = max(max_so_far, max_now)\n    return max_so_far\n"
  },
  {
    "path": "algorithms/dynamic_programming/min_cost_path.py",
    "content": "\"\"\"\nMinimum Cost Path\n\nFind the minimum cost to travel from station 0 to station N-1 given\na cost matrix where cost[i][j] is the price of going from station i\nto station j (for i < j).\n\nReference: https://en.wikipedia.org/wiki/Shortest_path_problem\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n_INF = float(\"inf\")\n\n\ndef min_cost(cost: list[list[int]]) -> int:\n    \"\"\"Compute the minimum cost to reach the last station from station 0.\n\n    Args:\n        cost: Square matrix where cost[i][j] is the travel cost from\n              station i to station j (for i < j).\n\n    Returns:\n        Minimum cost to reach station N-1 from station 0.\n\n    Examples:\n        >>> min_cost([[0, 15, 80, 90], [-1, 0, 40, 50],\n        ...          [-1, -1, 0, 70], [-1, -1, -1, 0]])\n        65\n    \"\"\"\n    length = len(cost)\n    dist = [_INF] * length\n\n    dist[0] = 0\n\n    for i in range(length):\n        for j in range(i + 1, length):\n            dist[j] = min(dist[j], dist[i] + cost[i][j])\n\n    return dist[length - 1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/num_decodings.py",
    "content": "\"\"\"\nDecode Ways\n\nGiven an encoded message of digits, count the total number of ways to\ndecode it where 'A' = 1, 'B' = 2, ..., 'Z' = 26.\n\nReference: https://leetcode.com/problems/decode-ways/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1) for num_decodings, O(n) for num_decodings2\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef num_decodings(enc_mes: str) -> int:\n    \"\"\"Count decoding ways using constant-space iteration.\n\n    Args:\n        enc_mes: String of digits representing the encoded message.\n\n    Returns:\n        Total number of ways to decode the message.\n\n    Examples:\n        >>> num_decodings(\"12\")\n        2\n        >>> num_decodings(\"226\")\n        3\n    \"\"\"\n    if not enc_mes or enc_mes[0] == \"0\":\n        return 0\n    last_char, last_two_chars = 1, 1\n    for i in range(1, len(enc_mes)):\n        last = last_char if enc_mes[i] != \"0\" else 0\n        last_two = (\n            last_two_chars\n            if int(enc_mes[i - 1 : i + 1]) < 27 and enc_mes[i - 1] != \"0\"\n            else 0\n        )\n        last_two_chars = last_char\n        last_char = last + last_two\n    return last_char\n\n\ndef num_decodings2(enc_mes: str) -> int:\n    \"\"\"Count decoding ways using a stack-based approach.\n\n    Args:\n        enc_mes: String of digits representing the encoded message.\n\n    Returns:\n        Total number of ways to decode the message.\n\n    Examples:\n        >>> num_decodings2(\"12\")\n        2\n        >>> num_decodings2(\"226\")\n        3\n    \"\"\"\n    if not enc_mes or enc_mes.startswith(\"0\"):\n        return 0\n    stack = [1, 1]\n    for i in range(1, len(enc_mes)):\n        if enc_mes[i] == \"0\":\n            if enc_mes[i - 1] == \"0\" or enc_mes[i - 1] > \"2\":\n                return 0\n            stack.append(stack[-2])\n        elif 9 < int(enc_mes[i - 1 : i + 1]) < 27:\n            stack.append(stack[-2] + stack[-1])\n        else:\n            stack.append(stack[-1])\n    return stack[-1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/planting_trees.py",
    "content": "\"\"\"\nPlanting Trees\n\nGiven an even number of trees along one side of a road, calculate the\nminimum total distance to move them into valid positions on both sides\nat even intervals.\n\nReference: https://en.wikipedia.org/wiki/Dynamic_programming\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom math import sqrt\n\n\ndef planting_trees(trees: list[int], length: int, width: int) -> float:\n    \"\"\"Compute the minimum distance to rearrange trees to valid positions.\n\n    Args:\n        trees: Sorted list of current tree positions along the road.\n        length: Length of the road.\n        width: Width of the road.\n\n    Returns:\n        Minimum total distance the trees must be moved.\n\n    Examples:\n        >>> planting_trees([0, 1, 10, 10], 10, 1)\n        2.414213562373095\n    \"\"\"\n    trees = [0] + trees\n\n    n_pairs = int(len(trees) / 2)\n\n    space_between_pairs = length / (n_pairs - 1)\n\n    target_locations = [location * space_between_pairs for location in range(n_pairs)]\n\n    cmatrix = [[0 for _ in range(n_pairs + 1)] for _ in range(n_pairs + 1)]\n    for r_i in range(1, n_pairs + 1):\n        cmatrix[r_i][0] = cmatrix[r_i - 1][0] + sqrt(\n            width + abs(trees[r_i] - target_locations[r_i - 1]) ** 2\n        )\n    for l_i in range(1, n_pairs + 1):\n        cmatrix[0][l_i] = cmatrix[0][l_i - 1] + abs(\n            trees[l_i] - target_locations[l_i - 1]\n        )\n\n    for r_i in range(1, n_pairs + 1):\n        for l_i in range(1, n_pairs + 1):\n            cmatrix[r_i][l_i] = min(\n                cmatrix[r_i - 1][l_i]\n                + sqrt(width + (trees[l_i + r_i] - target_locations[r_i - 1]) ** 2),\n                cmatrix[r_i][l_i - 1]\n                + abs(trees[l_i + r_i] - target_locations[l_i - 1]),\n            )\n\n    return cmatrix[n_pairs][n_pairs]\n"
  },
  {
    "path": "algorithms/dynamic_programming/regex_matching.py",
    "content": "\"\"\"\nRegular Expression Matching\n\nImplement regular expression matching with support for '.' (matches any\nsingle character) and '*' (matches zero or more of the preceding element).\n\nReference: https://leetcode.com/problems/regular-expression-matching/\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_match(str_a: str, str_b: str) -> bool:\n    \"\"\"Determine whether str_a matches the pattern str_b.\n\n    Args:\n        str_a: Input string.\n        str_b: Pattern string (may contain '.' and '*').\n\n    Returns:\n        True if str_a fully matches str_b, False otherwise.\n\n    Examples:\n        >>> is_match(\"aa\", \"a\")\n        False\n        >>> is_match(\"aa\", \"a*\")\n        True\n    \"\"\"\n    len_a, len_b = len(str_a) + 1, len(str_b) + 1\n    matches = [[False] * len_b for _ in range(len_a)]\n\n    matches[0][0] = True\n\n    for i, element in enumerate(str_b[1:], 2):\n        matches[0][i] = matches[0][i - 2] and element == \"*\"\n\n    for i, char_a in enumerate(str_a, 1):\n        for j, char_b in enumerate(str_b, 1):\n            if char_b != \"*\":\n                matches[i][j] = matches[i - 1][j - 1] and char_b in (char_a, \".\")\n            else:\n                matches[i][j] |= matches[i][j - 2]\n\n                if char_a == str_b[j - 2] or str_b[j - 2] == \".\":\n                    matches[i][j] |= matches[i - 1][j]\n\n    return matches[-1][-1]\n"
  },
  {
    "path": "algorithms/dynamic_programming/rod_cut.py",
    "content": "\"\"\"\nRod Cutting Problem\n\nGiven a rod of length n and a list of prices for each piece length,\ndetermine the maximum revenue obtainable by cutting and selling the pieces.\n\nReference: https://en.wikipedia.org/wiki/Cutting_stock_problem\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n_INT_MIN = -32767\n\n\ndef cut_rod(price: list[int]) -> int:\n    \"\"\"Compute the maximum obtainable value by cutting a rod optimally.\n\n    Args:\n        price: List where price[i] is the price of a piece of length i+1.\n\n    Returns:\n        Maximum revenue from cutting and selling the rod.\n\n    Examples:\n        >>> cut_rod([1, 5, 8, 9, 10, 17, 17, 20])\n        22\n    \"\"\"\n    n = len(price)\n    val = [0] * (n + 1)\n\n    for i in range(1, n + 1):\n        max_val = _INT_MIN\n        for j in range(i):\n            max_val = max(max_val, price[j] + val[i - j - 1])\n        val[i] = max_val\n\n    return val[n]\n"
  },
  {
    "path": "algorithms/dynamic_programming/word_break.py",
    "content": "\"\"\"\nWord Break\n\nGiven a string and a dictionary of words, determine whether the string\ncan be segmented into a sequence of dictionary words.\n\nReference: https://leetcode.com/problems/word-break/\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef word_break(word: str, word_dict: set[str]) -> bool:\n    \"\"\"Determine if word can be segmented into dictionary words.\n\n    Args:\n        word: The string to segment.\n        word_dict: Set of valid dictionary words.\n\n    Returns:\n        True if word can be segmented, False otherwise.\n\n    Examples:\n        >>> word_break(\"leetcode\", {\"leet\", \"code\"})\n        True\n        >>> word_break(\"catsandog\", {\"cats\", \"dog\", \"sand\", \"and\", \"cat\"})\n        False\n    \"\"\"\n    dp_array = [False] * (len(word) + 1)\n    dp_array[0] = True\n    for i in range(1, len(word) + 1):\n        for j in range(0, i):\n            if dp_array[j] and word[j:i] in word_dict:\n                dp_array[i] = True\n                break\n    return dp_array[-1]\n"
  },
  {
    "path": "algorithms/graph/__init__.py",
    "content": "\"\"\"\nCollection of graph algorithms.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.graph import DirectedEdge, DirectedGraph, Node\nfrom algorithms.graph.a_star import a_star\nfrom algorithms.graph.all_factors import (\n    get_factors,\n    get_factors_iterative1,\n    get_factors_iterative2,\n)\nfrom algorithms.graph.all_pairs_shortest_path import (\n    all_pairs_shortest_path,\n)\nfrom algorithms.graph.bellman_ford import bellman_ford\nfrom algorithms.graph.blossom import max_matching\nfrom algorithms.graph.check_bipartite import check_bipartite\nfrom algorithms.graph.clone_graph import (\n    UndirectedGraphNode,\n    clone_graph,\n    clone_graph1,\n    clone_graph2,\n)\nfrom algorithms.graph.count_islands_bfs import count_islands\nfrom algorithms.graph.count_islands_dfs import num_islands as num_islands_dfs\nfrom algorithms.graph.count_islands_unionfind import (\n    num_islands as num_islands_unionfind,\n)\nfrom algorithms.graph.dijkstra import Dijkstra\nfrom algorithms.graph.dijkstra_heapq import dijkstra\nfrom algorithms.graph.find_all_cliques import find_all_cliques\nfrom algorithms.graph.kahns_algorithm import Solution as KahnsSolution\nfrom algorithms.graph.markov_chain import iterating_markov_chain, next_state\nfrom algorithms.graph.maximum_flow import dinic, edmonds_karp, ford_fulkerson\nfrom algorithms.graph.maximum_flow_bfs import maximum_flow_bfs\nfrom algorithms.graph.maximum_flow_dfs import maximum_flow_dfs\nfrom algorithms.graph.maze_search_bfs import maze_search\nfrom algorithms.graph.maze_search_dfs import find_path as find_path_dfs\nfrom algorithms.graph.minimum_spanning_tree import DisjointSet, Edge, kruskal\nfrom algorithms.graph.pacific_atlantic import pacific_atlantic\nfrom algorithms.graph.prims_minimum_spanning import prims_minimum_spanning\nfrom algorithms.graph.satisfiability import solve_sat\nfrom algorithms.graph.shortest_distance_from_all_buildings import shortest_distance\nfrom algorithms.graph.sudoku_solver import Sudoku\nfrom algorithms.graph.tarjan import Tarjan\nfrom algorithms.graph.topological_sort_bfs import topological_sort\nfrom algorithms.graph.topological_sort_dfs import top_sort, top_sort_recursive\nfrom algorithms.graph.traversal import (\n    bfs_traverse,\n    dfs_traverse,\n    dfs_traverse_recursive,\n)\nfrom algorithms.graph.walls_and_gates import walls_and_gates\nfrom algorithms.graph.word_ladder import ladder_length\n\n__all__ = [\n    # a_star\n    \"a_star\",\n    # all_pairs_shortest_path\n    \"all_pairs_shortest_path\",\n    # bellman_ford\n    \"bellman_ford\",\n    # check_bipartite\n    \"check_bipartite\",\n    # clone_graph\n    \"UndirectedGraphNode\",\n    \"clone_graph\",\n    \"clone_graph1\",\n    \"clone_graph2\",\n    # dijkstra\n    \"Dijkstra\",\n    \"dijkstra\",\n    # find_all_cliques\n    \"find_all_cliques\",\n    # graph\n    \"DirectedEdge\",\n    \"DirectedGraph\",\n    \"Node\",\n    # kahns_algorithm\n    \"KahnsSolution\",\n    # markov_chain\n    \"iterating_markov_chain\",\n    \"next_state\",\n    # maximum_flow\n    \"dinic\",\n    \"edmonds_karp\",\n    \"ford_fulkerson\",\n    # maximum_flow_bfs\n    \"maximum_flow_bfs\",\n    # maximum_flow_dfs\n    \"maximum_flow_dfs\",\n    # minimum_spanning_tree\n    \"DisjointSet\",\n    \"Edge\",\n    \"kruskal\",\n    # prims_minimum_spanning\n    \"prims_minimum_spanning\",\n    # satisfiability\n    \"solve_sat\",\n    # tarjan\n    \"Tarjan\",\n    # traversal\n    \"bfs_traverse\",\n    \"dfs_traverse\",\n    \"dfs_traverse_recursive\",\n    # count_islands (bfs)\n    \"count_islands\",\n    # count_islands (dfs)\n    \"num_islands_dfs\",\n    # maze_search (bfs)\n    \"maze_search\",\n    # maze_search (dfs)\n    \"find_path_dfs\",\n    # word_ladder\n    \"ladder_length\",\n    # shortest_distance_from_all_buildings\n    \"shortest_distance\",\n    # topological_sort (bfs)\n    \"topological_sort\",\n    # all_factors\n    \"get_factors\",\n    \"get_factors_iterative1\",\n    \"get_factors_iterative2\",\n    # pacific_atlantic\n    \"pacific_atlantic\",\n    # sudoku_solver\n    \"Sudoku\",\n    # walls_and_gates\n    \"walls_and_gates\",\n    # topological_sort_dfs\n    \"top_sort\",\n    \"top_sort_recursive\",\n    # count_islands_unionfind\n    \"num_islands_unionfind\",\n    # blossom\n    \"max_matching\",\n]\n"
  },
  {
    "path": "algorithms/graph/a_star.py",
    "content": "\"\"\"\nA* (A-star) Search Algorithm\n\nFinds the shortest path in a weighted graph using a heuristic function.\n\nReference: https://en.wikipedia.org/wiki/A*_search_algorithm\n\nComplexity:\n    Time:  O(E log V) with a binary heap\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport heapq\nfrom collections.abc import Callable\nfrom typing import Any\n\n\ndef a_star(\n    graph: dict[Any, list[tuple[Any, float]]],\n    start: Any,\n    goal: Any,\n    h: Callable[[Any], float],\n) -> tuple[list[Any] | None, float]:\n    \"\"\"Find the shortest path using A* search.\n\n    Args:\n        graph: Adjacency list mapping node to list of (neighbor, cost) pairs.\n        start: Starting node.\n        goal: Goal node.\n        h: Heuristic function estimating cost from a node to the goal.\n\n    Returns:\n        A tuple (path, total_cost). If no path exists, returns (None, inf).\n\n    Examples:\n        >>> g = {'A': [('B', 1)], 'B': [('C', 2)], 'C': []}\n        >>> a_star(g, 'A', 'C', lambda n: 0)\n        (['A', 'B', 'C'], 3)\n    \"\"\"\n    open_set: list[tuple[float, float, Any, list[Any]]] = []\n    heapq.heappush(open_set, (h(start), 0, start, [start]))\n    visited: set[Any] = set()\n\n    while open_set:\n        f_score, g_score, current, path = heapq.heappop(open_set)\n        if current == goal:\n            return path, g_score\n\n        if current in visited:\n            continue\n        visited.add(current)\n\n        for neighbor, cost in graph.get(current, []):\n            if neighbor not in visited:\n                g = g_score + cost\n                f = g + h(neighbor)\n                heapq.heappush(open_set, (f, g, neighbor, path + [neighbor]))\n\n    return None, float(\"inf\")\n"
  },
  {
    "path": "algorithms/graph/all_factors.py",
    "content": "\"\"\"\nFactor Combinations\n\nGiven an integer n, return all possible combinations of its factors\n(excluding 1 and n itself in the factorisation).\n\nReference: https://leetcode.com/problems/factor-combinations/\n\nComplexity:\n    Time:  O(n^(1/2) * log n)  (approximate, depends on factor density)\n    Space: O(log n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef get_factors(n: int) -> list[list[int]]:\n    \"\"\"Return all factor combinations of *n* using recursion.\n\n    Args:\n        n: The number to factorise.\n\n    Returns:\n        List of factor lists.\n\n    Examples:\n        >>> get_factors(12)\n        [[2, 6], [2, 2, 3], [3, 4]]\n    \"\"\"\n\n    def _factor(\n        n: int,\n        i: int,\n        combi: list[int],\n        res: list[list[int]],\n    ) -> list[list[int]]:\n        while i * i <= n:\n            if n % i == 0:\n                res += (combi + [i, int(n / i)],)\n                _factor(n / i, i, combi + [i], res)\n            i += 1\n        return res\n\n    return _factor(n, 2, [], [])\n\n\ndef get_factors_iterative1(n: int) -> list[list[int]]:\n    \"\"\"Return all factor combinations using an explicit stack.\n\n    Args:\n        n: The number to factorise.\n\n    Returns:\n        List of factor lists.\n\n    Examples:\n        >>> get_factors_iterative1(12)\n        [[2, 6], [3, 4], [2, 2, 3]]\n    \"\"\"\n    todo: list[tuple[int, int, list[int]]] = [(n, 2, [])]\n    res: list[list[int]] = []\n    while todo:\n        n, i, combi = todo.pop()\n        while i * i <= n:\n            if n % i == 0:\n                res += (combi + [i, n // i],)\n                todo.append((n // i, i, combi + [i]))\n            i += 1\n    return res\n\n\ndef get_factors_iterative2(n: int) -> list[list[int]]:\n    \"\"\"Return all factor combinations using a stack-based approach.\n\n    Args:\n        n: The number to factorise.\n\n    Returns:\n        List of factor lists.\n\n    Examples:\n        >>> get_factors_iterative2(12)\n        [[2, 2, 3], [2, 6], [3, 4]]\n    \"\"\"\n    ans: list[list[int]] = []\n    stack: list[int] = []\n    x = 2\n    while True:\n        if x > n // x:\n            if not stack:\n                return ans\n            ans.append(stack + [n])\n            x = stack.pop()\n            n *= x\n            x += 1\n        elif n % x == 0:\n            stack.append(x)\n            n //= x\n        else:\n            x += 1\n"
  },
  {
    "path": "algorithms/graph/all_pairs_shortest_path.py",
    "content": "\"\"\"\nAll-Pairs Shortest Path (Floyd-Warshall)\n\nGiven an n*n adjacency matrix, computes the shortest path between every pair\nof vertices using the Floyd-Warshall algorithm.\n\nReference: https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm\n\nComplexity:\n    Time:  O(V^3)\n    Space: O(V^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport copy\n\n\ndef all_pairs_shortest_path(\n    adjacency_matrix: list[list[float]],\n) -> list[list[float]]:\n    \"\"\"Compute shortest distances between all pairs of vertices.\n\n    Args:\n        adjacency_matrix: An n*n matrix where entry [i][j] is the edge weight\n            from vertex i to vertex j.\n\n    Returns:\n        A new n*n matrix containing the shortest distance between each pair.\n\n    Examples:\n        >>> all_pairs_shortest_path(\n        ...     [[0, 1, float('inf')], [float('inf'), 0, 1],\n        ...      [1, float('inf'), 0]])\n        [[0, 1, 2], [2, 0, 1], [1, 2, 0]]\n    \"\"\"\n    new_array = copy.deepcopy(adjacency_matrix)\n\n    size = len(new_array)\n    for k in range(size):\n        for i in range(size):\n            for j in range(size):\n                if new_array[i][j] > new_array[i][k] + new_array[k][j]:\n                    new_array[i][j] = new_array[i][k] + new_array[k][j]\n\n    return new_array\n"
  },
  {
    "path": "algorithms/graph/bellman_ford.py",
    "content": "\"\"\"\nBellman-Ford Algorithm for Single-Source Shortest Path\n\nFinds the shortest paths from a source vertex to all other vertices in a\nweighted directed graph.  Unlike Dijkstra's algorithm it can handle graphs\nwith negative edge weights.\n\nReference: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm\n\nComplexity:\n    Time:  O(V * E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef bellman_ford(graph: dict[str, dict[str, float]], source: str) -> bool:\n    \"\"\"Compute shortest paths from *source* and detect negative cycles.\n\n    Args:\n        graph: Weighted directed graph as\n            ``{node: {neighbor: edge_weight, ...}, ...}``.\n        source: The starting vertex.\n\n    Returns:\n        True if shortest paths were computed (no negative cycle), False\n        otherwise.\n\n    Examples:\n        >>> g = {'a': {'b': 1}, 'b': {'c': 2}, 'c': {}}\n        >>> bellman_ford(g, 'a')\n        True\n    \"\"\"\n    distance: dict[str, float] = {}\n    predecessor: dict[str, str | None] = {}\n\n    _initialize_single_source(graph, source, distance, predecessor)\n\n    num_vertices = len(graph)\n    for _ in range(1, num_vertices):\n        for current_node in graph:\n            for neighbor in graph[current_node]:\n                edge_weight = graph[current_node][neighbor]\n                if distance[neighbor] > distance[current_node] + edge_weight:\n                    distance[neighbor] = distance[current_node] + edge_weight\n                    predecessor[neighbor] = current_node\n\n    for current_node in graph:\n        for neighbor in graph[current_node]:\n            edge_weight = graph[current_node][neighbor]\n            if distance[neighbor] > distance[current_node] + edge_weight:\n                return False\n\n    return True\n\n\ndef _initialize_single_source(\n    graph: dict[str, dict[str, float]],\n    source: str,\n    distance: dict[str, float],\n    predecessor: dict[str, str | None],\n) -> None:\n    \"\"\"Set up initial distances and predecessors.\n\n    Args:\n        graph: The weighted directed graph dictionary.\n        source: The source vertex.\n        distance: Dictionary to store shortest distances (modified in place).\n        predecessor: Dictionary to store path predecessors (modified in place).\n    \"\"\"\n    all_nodes: set[str] = set(graph.keys())\n    for neighbors in graph.values():\n        all_nodes.update(neighbors.keys())\n    for node in all_nodes:\n        distance[node] = float(\"inf\")\n        predecessor[node] = None\n    distance[source] = 0\n"
  },
  {
    "path": "algorithms/graph/blossom.py",
    "content": "\"\"\"Edmonds' blossom algorithm — maximum cardinality matching.\n\nFinds a maximum matching in a general (non-bipartite) undirected graph.\nThe algorithm handles odd-length cycles (\"blossoms\") by contracting them\nand recursing.\n\nTime: O(V^2 * E).\n\nInspired by PR #826 (abhishekiitm).\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef max_matching(n: int, edges: list[tuple[int, int]]) -> list[tuple[int, int]]:\n    \"\"\"Return a maximum cardinality matching for an undirected graph.\n\n    *n* is the number of vertices (0..n-1).\n    *edges* is a list of (u, v) edges.\n\n    Returns a list of matched (u, v) pairs.\n\n    >>> sorted(max_matching(4, [(0,1),(1,2),(2,3)]))\n    [(0, 1), (2, 3)]\n    \"\"\"\n    adj: list[list[int]] = [[] for _ in range(n)]\n    for u, v in edges:\n        adj[u].append(v)\n        adj[v].append(u)\n\n    match = [-1] * n\n\n    def find_augmenting_path() -> bool:\n        \"\"\"Try to find an augmenting path from any free vertex.\"\"\"\n        parent = [-1] * n\n        visited = [False] * n\n        for start in range(n):\n            if match[start] != -1:\n                continue\n            # BFS from this free vertex\n            queue: deque[int] = deque([start])\n            visited[start] = True\n            found = False\n            while queue and not found:\n                u = queue.popleft()\n                for v in adj[u]:\n                    if visited[v] or v == match[u]:\n                        continue\n                    if match[v] == -1 and v != start:\n                        # Augmenting path found — trace back and flip\n                        _augment(parent, match, u, v)\n                        return True\n                    if match[v] != -1:\n                        visited[v] = True\n                        visited[match[v]] = True\n                        parent[match[v]] = u\n                        queue.append(match[v])\n        return False\n\n    while find_augmenting_path():\n        pass\n\n    result = []\n    seen = set()\n    for i in range(n):\n        if match[i] != -1 and i not in seen:\n            result.append((i, match[i]))\n            seen.add(i)\n            seen.add(match[i])\n    return result\n\n\ndef _augment(parent: list[int], match: list[int], u: int, v: int) -> None:\n    \"\"\"Augment the matching along the path ending with edge (u, v).\"\"\"\n    while u != -1:\n        prev = match[u]\n        match[u] = v\n        match[v] = u\n        v = prev\n        u = parent[v] if v != -1 else -1\n"
  },
  {
    "path": "algorithms/graph/check_bipartite.py",
    "content": "\"\"\"\nCheck Bipartite Graph\n\nDetermine whether an undirected graph is bipartite using BFS colouring.\n\nReference: https://en.wikipedia.org/wiki/Bipartite_graph\n\nComplexity:\n    Time:  O(V^2)  (adjacency-matrix representation)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef check_bipartite(adj_list: list[list[int]]) -> bool:\n    \"\"\"Return True if the graph represented by *adj_list* is bipartite.\n\n    Args:\n        adj_list: An n*n adjacency matrix where adj_list[i][j] is truthy if\n            there is an edge between vertex *i* and vertex *j*.\n\n    Returns:\n        True if bipartite, False otherwise.\n\n    Examples:\n        >>> check_bipartite([[0, 1, 0], [1, 0, 1], [0, 1, 0]])\n        True\n    \"\"\"\n    vertices = len(adj_list)\n\n    set_type = [-1 for _ in range(vertices)]\n    set_type[0] = 0\n\n    queue = deque([0])\n\n    while queue:\n        current = queue.popleft()\n\n        if adj_list[current][current]:\n            return False\n\n        for adjacent in range(vertices):\n            if adj_list[current][adjacent]:\n                if set_type[adjacent] == set_type[current]:\n                    return False\n\n                if set_type[adjacent] == -1:\n                    set_type[adjacent] = 1 - set_type[current]\n                    queue.append(adjacent)\n\n    return True\n"
  },
  {
    "path": "algorithms/graph/check_digraph_strongly_connected.py",
    "content": "\"\"\"\nCheck if a Directed Graph is Strongly Connected\n\nA directed graph is strongly connected if every vertex is reachable from\nevery other vertex.  This implementation uses two DFS passes (one on the\noriginal graph and one on the reversed graph).\n\nReference: https://en.wikipedia.org/wiki/Strongly_connected_component\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V + E)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import defaultdict\n\n\nclass Graph:\n    \"\"\"A directed graph for strong-connectivity testing.\"\"\"\n\n    def __init__(self, vertex_count: int) -> None:\n        \"\"\"Create a new graph with *vertex_count* vertices.\n\n        Args:\n            vertex_count: Number of vertices (labelled 0 .. vertex_count-1).\n        \"\"\"\n        self.vertex_count = vertex_count\n        self.graph: dict[int, list[int]] = defaultdict(list)\n\n    def add_edge(self, source: int, target: int) -> None:\n        \"\"\"Add a directed edge from *source* to *target*.\n\n        Args:\n            source: Source vertex.\n            target: Target vertex.\n        \"\"\"\n        self.graph[source].append(target)\n\n    def dfs(self) -> bool:\n        \"\"\"Return True if all vertices are reachable from vertex 0.\"\"\"\n        visited = [False] * self.vertex_count\n        self._dfs_util(0, visited)\n        return visited == [True] * self.vertex_count\n\n    def _dfs_util(self, source: int, visited: list[bool]) -> None:\n        \"\"\"Recursive DFS helper.\n\n        Args:\n            source: Current vertex.\n            visited: Visited flags (modified in place).\n        \"\"\"\n        visited[source] = True\n        for adjacent in self.graph[source]:\n            if not visited[adjacent]:\n                self._dfs_util(adjacent, visited)\n\n    def reverse_graph(self) -> Graph:\n        \"\"\"Return a new graph with every edge reversed.\n\n        Returns:\n            A new Graph instance with reversed edges.\n        \"\"\"\n        reverse = Graph(self.vertex_count)\n        for source, adjacent in self.graph.items():\n            for target in adjacent:\n                reverse.add_edge(target, source)\n        return reverse\n\n    def is_strongly_connected(self) -> bool:\n        \"\"\"Return True if the graph is strongly connected.\n\n        Returns:\n            True when every vertex can reach every other vertex.\n        \"\"\"\n        if self.dfs():\n            reversed_graph = self.reverse_graph()\n            if reversed_graph.dfs():\n                return True\n        return False\n"
  },
  {
    "path": "algorithms/graph/clone_graph.py",
    "content": "\"\"\"\nClone an Undirected Graph\n\nEach node contains a label and a list of its neighbours.  Three strategies\nare provided: BFS-based, iterative DFS, and recursive DFS.\n\nReference: https://leetcode.com/problems/clone-graph/\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\nclass UndirectedGraphNode:\n    \"\"\"A node in an undirected graph.\"\"\"\n\n    def __init__(self, label: int) -> None:\n        self.label = label\n        self.neighbors: list[UndirectedGraphNode] = []\n\n    def shallow_copy(self) -> UndirectedGraphNode:\n        \"\"\"Return a copy of this node without neighbours.\n\n        Returns:\n            A new node with the same label.\n        \"\"\"\n        return UndirectedGraphNode(self.label)\n\n    def add_neighbor(self, node: UndirectedGraphNode) -> None:\n        \"\"\"Append *node* to the neighbour list.\n\n        Args:\n            node: Neighbour to add.\n        \"\"\"\n        self.neighbors.append(node)\n\n\ndef clone_graph1(node: UndirectedGraphNode | None) -> UndirectedGraphNode | None:\n    \"\"\"Clone a graph using BFS.\n\n    Args:\n        node: Any node in the original graph.\n\n    Returns:\n        The corresponding node in the cloned graph, or None.\n    \"\"\"\n    if not node:\n        return None\n    node_copy = node.shallow_copy()\n    dic: dict[UndirectedGraphNode, UndirectedGraphNode] = {node: node_copy}\n    queue: collections.deque[UndirectedGraphNode] = collections.deque([node])\n    while queue:\n        node = queue.popleft()\n        for neighbor in node.neighbors:\n            if neighbor not in dic:\n                neighbor_copy = neighbor.shallow_copy()\n                dic[neighbor] = neighbor_copy\n                dic[node].add_neighbor(neighbor_copy)\n                queue.append(neighbor)\n            else:\n                dic[node].add_neighbor(dic[neighbor])\n    return node_copy\n\n\ndef clone_graph2(node: UndirectedGraphNode | None) -> UndirectedGraphNode | None:\n    \"\"\"Clone a graph using iterative DFS.\n\n    Args:\n        node: Any node in the original graph.\n\n    Returns:\n        The corresponding node in the cloned graph, or None.\n    \"\"\"\n    if not node:\n        return None\n    node_copy = node.shallow_copy()\n    dic: dict[UndirectedGraphNode, UndirectedGraphNode] = {node: node_copy}\n    stack = [node]\n    while stack:\n        node = stack.pop()\n        for neighbor in node.neighbors:\n            if neighbor not in dic:\n                neighbor_copy = neighbor.shallow_copy()\n                dic[neighbor] = neighbor_copy\n                dic[node].add_neighbor(neighbor_copy)\n                stack.append(neighbor)\n            else:\n                dic[node].add_neighbor(dic[neighbor])\n    return node_copy\n\n\ndef clone_graph(node: UndirectedGraphNode | None) -> UndirectedGraphNode | None:\n    \"\"\"Clone a graph using recursive DFS.\n\n    Args:\n        node: Any node in the original graph.\n\n    Returns:\n        The corresponding node in the cloned graph, or None.\n    \"\"\"\n    if not node:\n        return None\n    node_copy = node.shallow_copy()\n    dic: dict[UndirectedGraphNode, UndirectedGraphNode] = {node: node_copy}\n    _dfs(node, dic)\n    return node_copy\n\n\ndef _dfs(\n    node: UndirectedGraphNode,\n    dic: dict[UndirectedGraphNode, UndirectedGraphNode],\n) -> None:\n    \"\"\"Recursively clone neighbours into *dic*.\n\n    Args:\n        node: Current node being cloned.\n        dic: Mapping from original nodes to their clones.\n    \"\"\"\n    for neighbor in node.neighbors:\n        if neighbor not in dic:\n            neighbor_copy = neighbor.shallow_copy()\n            dic[neighbor] = neighbor_copy\n            dic[node].add_neighbor(neighbor_copy)\n            _dfs(neighbor, dic)\n        else:\n            dic[node].add_neighbor(dic[neighbor])\n"
  },
  {
    "path": "algorithms/graph/count_connected_number_of_component.py",
    "content": "\"\"\"\nCount Connected Components in an Undirected Graph\n\nUses DFS to count the number of connected components.\n\nReference: https://en.wikipedia.org/wiki/Component_(graph_theory)\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count_components(adjacency_list: list[list[int]], size: int) -> int:\n    \"\"\"Return the number of connected components.\n\n    Args:\n        adjacency_list: Adjacency list where adjacency_list[i] contains\n            the neighbours of vertex *i* (1-indexed vertices).\n        size: Number of vertices.\n\n    Returns:\n        The count of connected components.\n\n    Examples:\n        >>> count_components([[], [2], [1]], 2)\n        1\n    \"\"\"\n    count = 0\n    visited = [False] * (size + 1)\n    for i in range(1, size + 1):\n        if not visited[i]:\n            _dfs(i, visited, adjacency_list)\n            count += 1\n    return count\n\n\ndef _dfs(\n    source: int,\n    visited: list[bool],\n    adjacency_list: list[list[int]],\n) -> None:\n    \"\"\"Mark all vertices reachable from *source* as visited.\n\n    Args:\n        source: Starting vertex.\n        visited: Visited flags (modified in place).\n        adjacency_list: Graph adjacency list.\n    \"\"\"\n    visited[source] = True\n    for child in adjacency_list[source]:\n        if not visited[child]:\n            _dfs(child, visited, adjacency_list)\n"
  },
  {
    "path": "algorithms/graph/count_islands_bfs.py",
    "content": "\"\"\"\nCount Islands (BFS)\n\nGiven a 2D grid of 1s (land) and 0s (water), count the number of islands\nusing breadth-first search.  An island is a group of adjacent lands\nconnected horizontally or vertically.\n\nReference: https://leetcode.com/problems/number-of-islands/\n\nComplexity:\n    Time:  O(M * N)\n    Space: O(M * N)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef count_islands(grid: list[list[int]]) -> int:\n    \"\"\"Return the number of islands in *grid*.\n\n    Args:\n        grid: 2D matrix of 0s and 1s.\n\n    Returns:\n        Number of connected components of 1s.\n\n    Examples:\n        >>> count_islands([[1, 0], [0, 1]])\n        2\n    \"\"\"\n    row = len(grid)\n    col = len(grid[0])\n\n    num_islands = 0\n    visited = [[0] * col for _ in range(row)]\n    directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]\n    queue: deque[tuple[int, int]] = deque()\n\n    for i in range(row):\n        for j, num in enumerate(grid[i]):\n            if num == 1 and visited[i][j] != 1:\n                visited[i][j] = 1\n                queue.append((i, j))\n                while queue:\n                    x, y = queue.popleft()\n                    for k in range(len(directions)):\n                        nx_x = x + directions[k][0]\n                        nx_y = y + directions[k][1]\n                        if (0 <= nx_x < row and 0 <= nx_y < col\n                                and visited[nx_x][nx_y] != 1\n                                and grid[nx_x][nx_y] == 1):\n                                queue.append((nx_x, nx_y))\n                                visited[nx_x][nx_y] = 1\n                num_islands += 1\n\n    return num_islands\n"
  },
  {
    "path": "algorithms/graph/count_islands_dfs.py",
    "content": "\"\"\"\nCount Islands (DFS)\n\nGiven a 2D grid of 1s (land) and 0s (water), count the number of islands\nusing depth-first search.\n\nReference: https://leetcode.com/problems/number-of-islands/\n\nComplexity:\n    Time:  O(M * N)\n    Space: O(M * N) recursion stack in worst case\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef num_islands(grid: list[list[int]]) -> int:\n    \"\"\"Return the number of islands in *grid*.\n\n    Args:\n        grid: 2D matrix of 0s and 1s (modified in place during traversal).\n\n    Returns:\n        Number of connected components of 1s.\n\n    Examples:\n        >>> num_islands([[1, 0], [0, 1]])\n        2\n    \"\"\"\n    count = 0\n    for i in range(len(grid)):\n        for j, col in enumerate(grid[i]):\n            if col == 1:\n                _dfs(grid, i, j)\n                count += 1\n    return count\n\n\ndef _dfs(grid: list[list[int]], i: int, j: int) -> None:\n    \"\"\"Flood-fill from (i, j), marking visited cells as 0.\n\n    Args:\n        grid: The grid (modified in place).\n        i: Row index.\n        j: Column index.\n    \"\"\"\n    if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]):\n        return\n    if grid[i][j] != 1:\n        return\n    grid[i][j] = 0\n    _dfs(grid, i + 1, j)\n    _dfs(grid, i - 1, j)\n    _dfs(grid, i, j + 1)\n    _dfs(grid, i, j - 1)\n"
  },
  {
    "path": "algorithms/graph/count_islands_unionfind.py",
    "content": "\"\"\"\nCount Islands via Union-Find\n\nUses the Union-Find (Disjoint Set) data structure to solve the \"Number of\nIslands\" problem. After each addLand operation, counts distinct connected\ncomponents of land cells.\n\nReference: https://en.wikipedia.org/wiki/Disjoint-set_data_structure\n\nComplexity:\n    Time:  O(m * alpha(m)) where m is number of positions\n    Space: O(m)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.union_find import Union\n\n\ndef num_islands(positions: list[list[int]]) -> list[int]:\n    \"\"\"Count islands after each addLand operation.\n\n    Given a sequence of positions on a 2D grid, each operation turns a water\n    cell into land. After each operation, count the number of distinct islands\n    (connected components of land cells).\n\n    Args:\n        positions: A list of [row, col] pairs indicating land additions.\n\n    Returns:\n        A list of island counts, one per operation.\n\n    Examples:\n        >>> num_islands([[0, 0], [0, 1], [1, 2], [2, 1]])\n        [1, 1, 2, 3]\n    \"\"\"\n    result: list[int] = []\n    islands = Union()\n    for position in map(tuple, positions):\n        islands.add(position)\n        for delta in (0, 1), (0, -1), (1, 0), (-1, 0):\n            adjacent = (position[0] + delta[0], position[1] + delta[1])\n            if adjacent in islands.parents:\n                islands.unite(position, adjacent)\n        result.append(islands.count)\n    return result\n"
  },
  {
    "path": "algorithms/graph/cycle_detection.py",
    "content": "\"\"\"\nCycle Detection in a Directed Graph\n\nUses DFS with three-colour marking to determine whether a directed graph\ncontains a cycle.\n\nReference: https://en.wikipedia.org/wiki/Cycle_(graph_theory)\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom enum import Enum\n\n\nclass TraversalState(Enum):\n    \"\"\"Vertex states during DFS traversal.\"\"\"\n\n    WHITE = 0\n    GRAY = 1\n    BLACK = 2\n\n\ndef is_in_cycle(\n    graph: dict[str, list[str]],\n    traversal_states: dict[str, TraversalState],\n    vertex: str,\n) -> bool:\n    \"\"\"Return True if *vertex* is part of a cycle.\n\n    Args:\n        graph: Adjacency list of a directed graph.\n        traversal_states: Current DFS colour for each vertex.\n        vertex: Vertex to inspect.\n\n    Returns:\n        True if a cycle is detected through *vertex*.\n    \"\"\"\n    if traversal_states[vertex] == TraversalState.GRAY:\n        return True\n    traversal_states[vertex] = TraversalState.GRAY\n    for neighbor in graph[vertex]:\n        if is_in_cycle(graph, traversal_states, neighbor):\n            return True\n    traversal_states[vertex] = TraversalState.BLACK\n    return False\n\n\ndef contains_cycle(graph: dict[str, list[str]]) -> bool:\n    \"\"\"Return True if *graph* contains at least one cycle.\n\n    Args:\n        graph: Directed graph as ``{vertex: [neighbours], ...}``.\n\n    Returns:\n        True when a cycle exists.\n\n    Examples:\n        >>> contains_cycle({'A': ['B'], 'B': ['A']})\n        True\n        >>> contains_cycle({'A': ['B'], 'B': []})\n        False\n    \"\"\"\n    traversal_states = {vertex: TraversalState.WHITE for vertex in graph}\n    for vertex, state in traversal_states.items():\n        if state == TraversalState.WHITE and is_in_cycle(\n            graph, traversal_states, vertex\n        ):\n            return True\n    return False\n"
  },
  {
    "path": "algorithms/graph/dijkstra.py",
    "content": "\"\"\"\nDijkstra's Single-Source Shortest-Path Algorithm\n\nFinds shortest distances from a source vertex to every other vertex in a\ngraph with non-negative edge weights.\n\nReference: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm\n\nComplexity:\n    Time:  O(V^2)  (adjacency-matrix representation)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Dijkstra:\n    \"\"\"A fully connected directed graph with edge weights.\"\"\"\n\n    def __init__(self, vertex_count: int) -> None:\n        \"\"\"Initialise graph with *vertex_count* vertices.\n\n        Args:\n            vertex_count: Number of vertices.\n        \"\"\"\n        self.vertex_count = vertex_count\n        self.graph: list[list[int]] = [\n            [0 for _ in range(vertex_count)] for _ in range(vertex_count)\n        ]\n\n    def min_distance(self, dist: list[float], min_dist_set: list[bool]) -> int:\n        \"\"\"Return the unvisited vertex with the smallest distance.\n\n        Args:\n            dist: Current shortest distances.\n            min_dist_set: Flags indicating already-processed vertices.\n\n        Returns:\n            Index of the closest unvisited vertex.\n        \"\"\"\n        min_dist = float(\"inf\")\n        min_index = 0\n        for target in range(self.vertex_count):\n            if min_dist_set[target]:\n                continue\n            if dist[target] < min_dist:\n                min_dist = dist[target]\n                min_index = target\n        return min_index\n\n    def dijkstra(self, src: int) -> list[float]:\n        \"\"\"Compute shortest distances from *src* to all other vertices.\n\n        Args:\n            src: Source vertex index.\n\n        Returns:\n            List of shortest distances indexed by vertex.\n\n        Examples:\n            >>> g = Dijkstra(3)\n            >>> g.graph = [[0, 1, 4], [1, 0, 2], [4, 2, 0]]\n            >>> g.dijkstra(0)\n            [0, 1, 3]\n        \"\"\"\n        dist: list[float] = [float(\"inf\")] * self.vertex_count\n        dist[src] = 0\n        min_dist_set = [False] * self.vertex_count\n\n        for _ in range(self.vertex_count):\n            source = self.min_distance(dist, min_dist_set)\n            min_dist_set[source] = True\n\n            for target in range(self.vertex_count):\n                if self.graph[source][target] <= 0 or min_dist_set[target]:\n                    continue\n                if dist[target] > dist[source] + self.graph[source][target]:\n                    dist[target] = dist[source] + self.graph[source][target]\n\n        return dist\n"
  },
  {
    "path": "algorithms/graph/dijkstra_heapq.py",
    "content": "\"\"\"\nDijkstra's Shortest-Path Algorithm (Heap-Optimised)\n\nComputes single-source shortest paths in a graph with non-negative edge\nweights using a min-heap (priority queue) for efficient vertex selection.\n\nThis adjacency-list implementation is faster than the O(V²) matrix version\nfor sparse graphs.\n\nReference: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm\n\nComplexity:\n    Time:  O((V + E) log V)  using a binary heap\n    Space: O(V + E)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport heapq\n\n\ndef dijkstra(\n    graph: dict[str, dict[str, int | float]],\n    source: str,\n    target: str = \"\",\n) -> tuple[int | float, list[str]]:\n    \"\"\"Return the shortest distance and path from *source* to *target*.\n\n    Args:\n        graph: Adjacency-list mapping each vertex to a dict of\n               {neighbour: weight}.\n        source: Starting vertex.\n        target: Destination vertex.  When empty, compute shortest\n                distances to all reachable vertices and return the path\n                to the last vertex relaxed (mainly useful when a target\n                is provided).\n\n    Returns:\n        A ``(distance, path)`` tuple where *distance* is the total\n        shortest-path cost and *path* is the list of vertices from\n        *source* to *target* inclusive.  If *target* is unreachable the\n        distance is ``float('inf')`` and the path is empty.\n\n    Examples:\n        >>> g = {\n        ...     \"s\": {\"a\": 2, \"b\": 1},\n        ...     \"a\": {\"s\": 3, \"b\": 4, \"c\": 8},\n        ...     \"b\": {\"s\": 4, \"a\": 2, \"d\": 2},\n        ...     \"c\": {\"a\": 2, \"d\": 7, \"t\": 4},\n        ...     \"d\": {\"b\": 1, \"c\": 11, \"t\": 5},\n        ...     \"t\": {\"c\": 3, \"d\": 5},\n        ... }\n        >>> dijkstra(g, \"s\", \"t\")\n        (8, ['s', 'b', 'd', 't'])\n    \"\"\"\n    dist: dict[str, int | float] = {v: float(\"inf\") for v in graph}\n    dist[source] = 0\n    prev: dict[str, str | None] = {v: None for v in graph}\n    heap: list[tuple[int | float, str]] = [(0, source)]\n\n    while heap:\n        d, u = heapq.heappop(heap)\n        if d > dist[u]:\n            continue\n        if u == target:\n            break\n        for v, weight in graph[u].items():\n            alt = dist[u] + weight\n            if alt < dist.get(v, float(\"inf\")):\n                dist[v] = alt\n                prev[v] = u\n                heapq.heappush(heap, (alt, v))\n\n    # Reconstruct path\n    path: list[str] = []\n    node: str | None = target if target else None\n    if node and dist.get(node, float(\"inf\")) < float(\"inf\"):\n        while node is not None:\n            path.append(node)\n            node = prev.get(node)\n        path.reverse()\n\n    return (dist.get(target, float(\"inf\")), path) if target else (0, [])\n"
  },
  {
    "path": "algorithms/graph/find_all_cliques.py",
    "content": "\"\"\"\nFind All Cliques (Bron-Kerbosch)\n\nFinds every maximal clique in an undirected graph.\n\nReference: Bron, Coen; Kerbosch, Joep (1973), \"Algorithm 457: finding all\n    cliques of an undirected graph\", Communications of the ACM.\n\nComplexity:\n    Time:  O(3^(V/3)) worst case\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_all_cliques(edges: dict[str, set[str]]) -> list[list[str]]:\n    \"\"\"Return all maximal cliques in the graph.\n\n    Args:\n        edges: Adjacency sets keyed by vertex label.\n\n    Returns:\n        A list of cliques, where each clique is a list of vertex labels.\n\n    Examples:\n        >>> find_all_cliques({'0': {'1'}, '1': {'0'}})\n        [['0', '1']]\n    \"\"\"\n    compsub: list[str] = []\n    solutions: list[list[str]] = []\n\n    def _expand_clique(candidates: set[str], nays: set[str]) -> None:\n        if not candidates and not nays:\n            solutions.append(compsub.copy())\n        else:\n            for selected in candidates.copy():\n                candidates.remove(selected)\n                candidates_temp = _get_connected(selected, candidates)\n                nays_temp = _get_connected(selected, nays)\n                compsub.append(selected)\n                _expand_clique(candidates_temp, nays_temp)\n                nays.add(compsub.pop())\n\n    def _get_connected(vertex: str, old_set: set[str]) -> set[str]:\n        new_set: set[str] = set()\n        for neighbor in edges[str(vertex)]:\n            if neighbor in old_set:\n                new_set.add(neighbor)\n        return new_set\n\n    possibles = set(edges.keys())\n    _expand_clique(possibles, set())\n    return solutions\n"
  },
  {
    "path": "algorithms/graph/find_path.py",
    "content": "\"\"\"\nFind Paths in a Graph\n\nProvides functions to find a single path, all paths, or the shortest path\nbetween two nodes using recursion and backtracking.\n\nComplexity:\n    Time:  O(V!) worst case (exponential backtracking)\n    Space: O(V) per recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\n\ndef find_path(\n    graph: dict[Any, list[Any]],\n    start: Any,\n    end: Any,\n    path: list[Any] | None = None,\n) -> list[Any] | None:\n    \"\"\"Find a path between *start* and *end* using backtracking.\n\n    Args:\n        graph: Adjacency list.\n        start: Source node.\n        end: Target node.\n        path: Accumulated path (internal use).\n\n    Returns:\n        A list representing the path, or None if no path exists.\n\n    Examples:\n        >>> find_path({'A': ['B'], 'B': ['C'], 'C': []}, 'A', 'C')\n        ['A', 'B', 'C']\n    \"\"\"\n    if path is None:\n        path = []\n    path = path + [start]\n    if start == end:\n        return path\n    if start not in graph:\n        return None\n    for node in graph[start]:\n        if node not in path:\n            newpath = find_path(graph, node, end, path)\n            return newpath\n    return None\n\n\ndef find_all_path(\n    graph: dict[Any, list[Any]],\n    start: Any,\n    end: Any,\n    path: list[Any] | None = None,\n) -> list[list[Any]]:\n    \"\"\"Find all paths between *start* and *end*.\n\n    Args:\n        graph: Adjacency list.\n        start: Source node.\n        end: Target node.\n        path: Accumulated path (internal use).\n\n    Returns:\n        A list of all paths, where each path is a list of nodes.\n\n    Examples:\n        >>> find_all_path({'A': ['B', 'C'], 'B': ['C'], 'C': []}, 'A', 'C')\n        [['A', 'B', 'C'], ['A', 'C']]\n    \"\"\"\n    if path is None:\n        path = []\n    path = path + [start]\n    if start == end:\n        return [path]\n    if start not in graph:\n        return []\n    paths: list[list[Any]] = []\n    for node in graph[start]:\n        if node not in path:\n            newpaths = find_all_path(graph, node, end, path)\n            for newpath in newpaths:\n                paths.append(newpath)\n    return paths\n\n\ndef find_shortest_path(\n    graph: dict[Any, list[Any]],\n    start: Any,\n    end: Any,\n    path: list[Any] | None = None,\n) -> list[Any] | None:\n    \"\"\"Find the shortest path between *start* and *end*.\n\n    Args:\n        graph: Adjacency list.\n        start: Source node.\n        end: Target node.\n        path: Accumulated path (internal use).\n\n    Returns:\n        The shortest path as a list of nodes, or None if unreachable.\n\n    Examples:\n        >>> find_shortest_path({'A': ['B', 'C'], 'B': ['C'], 'C': []}, 'A', 'C')\n        ['A', 'C']\n    \"\"\"\n    if path is None:\n        path = []\n    path = path + [start]\n    if start == end:\n        return path\n    if start not in graph:\n        return None\n    shortest: list[Any] | None = None\n    for node in graph[start]:\n        if node not in path:\n            newpath = find_shortest_path(graph, node, end, path)\n            if newpath and (not shortest or len(newpath) < len(shortest)):\n                    shortest = newpath\n    return shortest\n"
  },
  {
    "path": "algorithms/graph/graph.py",
    "content": "\"\"\"Graph Data Structures (re-export).\n\nThis module re-exports Node, DirectedEdge, and DirectedGraph from the\ncanonical location in ``algorithms.data_structures.graph`` for backward\ncompatibility.\n\"\"\"\n\nfrom algorithms.data_structures.graph import DirectedEdge, DirectedGraph, Node\n\n__all__ = [\"DirectedEdge\", \"DirectedGraph\", \"Node\"]\n"
  },
  {
    "path": "algorithms/graph/kahns_algorithm.py",
    "content": "\"\"\"\nKahn's Algorithm (Topological Sort via BFS)\n\nComputes a topological ordering of a directed acyclic graph using an\nin-degree based BFS approach.\n\nReference: https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\nclass Solution:\n    \"\"\"Wrapper class for Kahn's topological sort.\"\"\"\n\n    def topological_sort(\n        self, vertices: int, adj: list[list[int]]\n    ) -> list[int]:\n        \"\"\"Return a topological ordering of the graph.\n\n        Args:\n            vertices: Number of vertices.\n            adj: Adjacency list where adj[i] lists neighbours of vertex *i*.\n\n        Returns:\n            A list of vertices in topological order, or an empty list if a\n            cycle is detected.\n\n        Examples:\n            >>> Solution().topological_sort(3, [[1], [2], []])\n            [0, 1, 2]\n        \"\"\"\n        in_degree = [0] * vertices\n        for i in range(vertices):\n            for neighbor in adj[i]:\n                in_degree[neighbor] += 1\n\n        queue = deque(\n            [i for i in range(vertices) if in_degree[i] == 0]\n        )\n        topo_order: list[int] = []\n\n        while queue:\n            node = queue.popleft()\n            topo_order.append(node)\n            for neighbor in adj[node]:\n                in_degree[neighbor] -= 1\n                if in_degree[neighbor] == 0:\n                    queue.append(neighbor)\n\n        if len(topo_order) != vertices:\n            return []\n\n        return topo_order\n"
  },
  {
    "path": "algorithms/graph/markov_chain.py",
    "content": "\"\"\"\nMarkov Chain\n\nProvides utilities for stepping through and iterating a discrete Markov chain\ndescribed as a dictionary of transition probabilities.\n\nReference: https://en.wikipedia.org/wiki/Markov_chain\n\nComplexity:\n    Time:  O(S) per step, where S is the number of states\n    Space: O(S)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport random\nfrom collections.abc import Iterator\nfrom typing import Any\n\n\ndef _choose_state(state_map: dict[Any, float]) -> Any | None:\n    \"\"\"Choose the next state randomly according to *state_map*.\n\n    Args:\n        state_map: Mapping of state to its transition probability.\n\n    Returns:\n        The selected state, or None if probabilities don't sum to 1.\n    \"\"\"\n    choice = random.random()\n    probability_reached = 0.0\n    for state, probability in state_map.items():\n        probability_reached += probability\n        if probability_reached > choice:\n            return state\n    return None\n\n\ndef next_state(chain: dict[Any, dict[Any, float]], current_state: Any) -> Any:\n    \"\"\"Return the next state given a Markov chain and current state.\n\n    Args:\n        chain: Markov chain as ``{state: {next_state: probability}}``.\n        current_state: The current state.\n\n    Returns:\n        The randomly chosen next state.\n\n    Examples:\n        >>> c = {'A': {'A': 1.0}}\n        >>> next_state(c, 'A')\n        'A'\n    \"\"\"\n    next_state_map = chain.get(current_state)\n    return _choose_state(next_state_map)\n\n\ndef iterating_markov_chain(\n    chain: dict[Any, dict[Any, float]],\n    state: Any,\n) -> Iterator[Any]:\n    \"\"\"Yield an infinite sequence of states from a Markov chain.\n\n    Args:\n        chain: Markov chain transition dictionary.\n        state: Initial state.\n\n    Yields:\n        Successive states of the chain.\n    \"\"\"\n    while True:\n        state = next_state(chain, state)\n        yield state\n"
  },
  {
    "path": "algorithms/graph/maximum_flow.py",
    "content": "\"\"\"\nMaximum Flow Algorithms\n\nImplements Ford-Fulkerson (DFS), Edmonds-Karp (BFS) and Dinic's algorithm\nfor computing maximum flow in a flow network.\n\nReference: https://en.wikipedia.org/wiki/Maximum_flow_problem\n\nComplexity:\n    Ford-Fulkerson: O(E * f)  where f is the max flow value\n    Edmonds-Karp:   O(V * E^2)\n    Dinic:          O(V^2 * E)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom queue import Queue\n\n\ndef _dfs(\n    capacity: list[list[int]],\n    flow: list[list[int]],\n    visit: list[bool],\n    vertices: int,\n    idx: int,\n    sink: int,\n    current_flow: int = 1 << 63,\n) -> int:\n    \"\"\"DFS helper for Ford-Fulkerson.\n\n    Args:\n        capacity: Capacity matrix.\n        flow: Current flow matrix.\n        visit: Visited flags.\n        vertices: Total number of vertices.\n        idx: Current vertex index.\n        sink: Sink vertex index.\n        current_flow: Flow available along this path.\n\n    Returns:\n        Flow pushed along the augmenting path found.\n    \"\"\"\n    if idx == sink:\n        return current_flow\n    visit[idx] = True\n    for nxt in range(vertices):\n        if not visit[nxt] and flow[idx][nxt] < capacity[idx][nxt]:\n            available_flow = min(current_flow, capacity[idx][nxt] - flow[idx][nxt])\n            tmp = _dfs(capacity, flow, visit, vertices, nxt, sink, available_flow)\n            if tmp:\n                flow[idx][nxt] += tmp\n                flow[nxt][idx] -= tmp\n                return tmp\n    return 0\n\n\ndef ford_fulkerson(capacity: list[list[int]], source: int, sink: int) -> int:\n    \"\"\"Compute maximum flow using Ford-Fulkerson (DFS).\n\n    Args:\n        capacity: Capacity matrix.\n        source: Source vertex.\n        sink: Sink vertex.\n\n    Returns:\n        The maximum flow value.\n\n    Examples:\n        >>> ford_fulkerson([[0, 10, 0], [0, 0, 10], [0, 0, 0]], 0, 2)\n        10\n    \"\"\"\n    vertices = len(capacity)\n    ret = 0\n    flow = [[0] * vertices for _ in range(vertices)]\n    while True:\n        visit = [False for _ in range(vertices)]\n        tmp = _dfs(capacity, flow, visit, vertices, source, sink)\n        if tmp:\n            ret += tmp\n        else:\n            break\n    return ret\n\n\ndef edmonds_karp(capacity: list[list[int]], source: int, sink: int) -> int:\n    \"\"\"Compute maximum flow using Edmonds-Karp (BFS).\n\n    Args:\n        capacity: Capacity matrix.\n        source: Source vertex.\n        sink: Sink vertex.\n\n    Returns:\n        The maximum flow value.\n\n    Examples:\n        >>> edmonds_karp([[0, 10, 0], [0, 0, 10], [0, 0, 0]], 0, 2)\n        10\n    \"\"\"\n    vertices = len(capacity)\n    ret = 0\n    flow = [[0] * vertices for _ in range(vertices)]\n    while True:\n        tmp = 0\n        queue: Queue[tuple[int, int]] = Queue()\n        visit = [False for _ in range(vertices)]\n        par = [-1 for _ in range(vertices)]\n        visit[source] = True\n        queue.put((source, 1 << 63))\n        while queue.qsize():\n            front = queue.get()\n            idx, current_flow = front\n            if idx == sink:\n                tmp = current_flow\n                break\n            for nxt in range(vertices):\n                if not visit[nxt] and flow[idx][nxt] < capacity[idx][nxt]:\n                    visit[nxt] = True\n                    par[nxt] = idx\n                    queue.put(\n                        (nxt, min(current_flow, capacity[idx][nxt] - flow[idx][nxt]))\n                    )\n        if par[sink] == -1:\n            break\n        ret += tmp\n        parent = par[sink]\n        idx = sink\n        while parent != -1:\n            flow[parent][idx] += tmp\n            flow[idx][parent] -= tmp\n            idx = parent\n            parent = par[parent]\n    return ret\n\n\ndef _dinic_bfs(\n    capacity: list[list[int]],\n    flow: list[list[int]],\n    level: list[int],\n    source: int,\n    sink: int,\n) -> bool:\n    \"\"\"BFS level graph construction for Dinic's algorithm.\n\n    Args:\n        capacity: Capacity matrix.\n        flow: Current flow matrix.\n        level: Level array (modified in place).\n        source: Source vertex.\n        sink: Sink vertex.\n\n    Returns:\n        True if sink is reachable from source.\n    \"\"\"\n    vertices = len(capacity)\n    queue: Queue[int] = Queue()\n    queue.put(source)\n    level[source] = 0\n    while queue.qsize():\n        front = queue.get()\n        for nxt in range(vertices):\n            if level[nxt] == -1 and flow[front][nxt] < capacity[front][nxt]:\n                level[nxt] = level[front] + 1\n                queue.put(nxt)\n    return level[sink] != -1\n\n\ndef _dinic_dfs(\n    capacity: list[list[int]],\n    flow: list[list[int]],\n    level: list[int],\n    idx: int,\n    sink: int,\n    work: list[int],\n    current_flow: int = 1 << 63,\n) -> int:\n    \"\"\"DFS blocking flow for Dinic's algorithm.\n\n    Args:\n        capacity: Capacity matrix.\n        flow: Current flow matrix.\n        level: Level array.\n        idx: Current vertex.\n        sink: Sink vertex.\n        work: Work pointer array.\n        current_flow: Available flow.\n\n    Returns:\n        Flow pushed.\n    \"\"\"\n    if idx == sink:\n        return current_flow\n    vertices = len(capacity)\n    while work[idx] < vertices:\n        nxt = work[idx]\n        if level[nxt] == level[idx] + 1 and flow[idx][nxt] < capacity[idx][nxt]:\n            available_flow = min(current_flow, capacity[idx][nxt] - flow[idx][nxt])\n            tmp = _dinic_dfs(capacity, flow, level, nxt, sink, work, available_flow)\n            if tmp > 0:\n                flow[idx][nxt] += tmp\n                flow[nxt][idx] -= tmp\n                return tmp\n        work[idx] += 1\n    return 0\n\n\ndef dinic(capacity: list[list[int]], source: int, sink: int) -> int:\n    \"\"\"Compute maximum flow using Dinic's algorithm.\n\n    Args:\n        capacity: Capacity matrix.\n        source: Source vertex.\n        sink: Sink vertex.\n\n    Returns:\n        The maximum flow value.\n\n    Examples:\n        >>> dinic([[0, 10, 0], [0, 0, 10], [0, 0, 0]], 0, 2)\n        10\n    \"\"\"\n    vertices = len(capacity)\n    flow = [[0] * vertices for _ in range(vertices)]\n    ret = 0\n    while True:\n        level = [-1 for _ in range(vertices)]\n        work = [0 for _ in range(vertices)]\n        if not _dinic_bfs(capacity, flow, level, source, sink):\n            break\n        while True:\n            tmp = _dinic_dfs(capacity, flow, level, source, sink, work)\n            if tmp > 0:\n                ret += tmp\n            else:\n                break\n    return ret\n"
  },
  {
    "path": "algorithms/graph/maximum_flow_bfs.py",
    "content": "\"\"\"\nMaximum Flow via BFS\n\nComputes maximum flow in a network represented as an adjacency matrix,\nusing BFS to find augmenting paths.\n\nReference: https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\n\nComplexity:\n    Time:  O(V * E^2)\n    Space: O(V^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport copy\nimport math\nimport queue\n\n\ndef maximum_flow_bfs(adjacency_matrix: list[list[int]]) -> int:\n    \"\"\"Compute maximum flow using BFS augmenting paths.\n\n    The source is the first vertex and the sink is the last vertex.\n\n    Args:\n        adjacency_matrix: n*n capacity matrix.\n\n    Returns:\n        The maximum flow value.\n\n    Examples:\n        >>> maximum_flow_bfs([[0, 10, 0], [0, 0, 10], [0, 0, 0]])\n        10\n    \"\"\"\n    new_array = copy.deepcopy(adjacency_matrix)\n    total = 0\n\n    while True:\n        min_flow = math.inf\n        visited = [0] * len(new_array)\n        path = [0] * len(new_array)\n\n        bfs: queue.Queue[int] = queue.Queue()\n\n        visited[0] = 1\n        bfs.put(0)\n\n        while bfs.qsize() > 0:\n            src = bfs.get()\n            for k in range(len(new_array)):\n                if new_array[src][k] > 0 and visited[k] == 0:\n                    visited[k] = 1\n                    bfs.put(k)\n                    path[k] = src\n\n        if visited[len(new_array) - 1] == 0:\n            break\n\n        tmp = len(new_array) - 1\n\n        while tmp != 0:\n            if min_flow > new_array[path[tmp]][tmp]:\n                min_flow = new_array[path[tmp]][tmp]\n            tmp = path[tmp]\n\n        tmp = len(new_array) - 1\n\n        while tmp != 0:\n            new_array[path[tmp]][tmp] = new_array[path[tmp]][tmp] - min_flow\n            tmp = path[tmp]\n\n        total = total + min_flow\n\n    return total\n"
  },
  {
    "path": "algorithms/graph/maximum_flow_dfs.py",
    "content": "\"\"\"\nMaximum Flow via DFS\n\nComputes maximum flow in a network represented as an adjacency matrix,\nusing DFS to find augmenting paths.\n\nReference: https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\n\nComplexity:\n    Time:  O(E * f)  where f is the max flow value\n    Space: O(V^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport copy\nimport math\n\n\ndef maximum_flow_dfs(adjacency_matrix: list[list[int]]) -> int:\n    \"\"\"Compute maximum flow using DFS augmenting paths.\n\n    The source is the first vertex and the sink is the last vertex.\n\n    Args:\n        adjacency_matrix: n*n capacity matrix.\n\n    Returns:\n        The maximum flow value.\n\n    Examples:\n        >>> maximum_flow_dfs([[0, 10, 0], [0, 0, 10], [0, 0, 0]])\n        10\n    \"\"\"\n    new_array = copy.deepcopy(adjacency_matrix)\n    total = 0\n\n    while True:\n        min_flow = math.inf\n        visited = [0] * len(new_array)\n        path = [0] * len(new_array)\n\n        stack: list[int] = []\n\n        visited[0] = 1\n        stack.append(0)\n\n        while len(stack) > 0:\n            src = stack.pop()\n            for k in range(len(new_array)):\n                if new_array[src][k] > 0 and visited[k] == 0:\n                    visited[k] = 1\n                    stack.append(k)\n                    path[k] = src\n\n        if visited[len(new_array) - 1] == 0:\n            break\n\n        tmp = len(new_array) - 1\n\n        while tmp != 0:\n            if min_flow > new_array[path[tmp]][tmp]:\n                min_flow = new_array[path[tmp]][tmp]\n            tmp = path[tmp]\n\n        tmp = len(new_array) - 1\n\n        while tmp != 0:\n            new_array[path[tmp]][tmp] = new_array[path[tmp]][tmp] - min_flow\n            tmp = path[tmp]\n\n        total = total + min_flow\n\n    return total\n"
  },
  {
    "path": "algorithms/graph/maze_search_bfs.py",
    "content": "\"\"\"\nMaze Search (BFS)\n\nFind the minimum number of steps from the top-left corner to the\nbottom-right corner of a grid.  Only cells with value 1 may be traversed.\nReturns -1 if no path exists.\n\nComplexity:\n    Time:  O(M * N)\n    Space: O(M * N)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef maze_search(maze: list[list[int]]) -> int:\n    \"\"\"Return the shortest path length in *maze*, or -1 if unreachable.\n\n    Args:\n        maze: 2D grid where 1 = passable, 0 = blocked.\n\n    Returns:\n        Minimum steps from (0,0) to (height-1, width-1), or -1.\n\n    Examples:\n        >>> maze_search([[1, 1], [1, 1]])\n        2\n        >>> maze_search([[1, 0], [0, 1]])\n        -1\n    \"\"\"\n    blocked, allowed = 0, 1\n    unvisited, visited = 0, 1\n\n    initial_x, initial_y = 0, 0\n\n    if maze[initial_x][initial_y] == blocked:\n        return -1\n\n    directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]\n\n    height, width = len(maze), len(maze[0])\n\n    target_x, target_y = height - 1, width - 1\n\n    queue = deque([(initial_x, initial_y, 0)])\n\n    is_visited = [[unvisited for _ in range(width)] for _ in range(height)]\n    is_visited[initial_x][initial_y] = visited\n\n    while queue:\n        x, y, steps = queue.popleft()\n\n        if x == target_x and y == target_y:\n            return steps\n\n        for dx, dy in directions:\n            new_x = x + dx\n            new_y = y + dy\n\n            if not (0 <= new_x < height and 0 <= new_y < width):\n                continue\n\n            if maze[new_x][new_y] == allowed and is_visited[new_x][new_y] == unvisited:\n                queue.append((new_x, new_y, steps + 1))\n                is_visited[new_x][new_y] = visited\n\n    return -1\n"
  },
  {
    "path": "algorithms/graph/maze_search_dfs.py",
    "content": "\"\"\"\nMaze Search (DFS)\n\nFind the shortest path from the top-left corner to the bottom-right corner\nof a grid using depth-first search with backtracking.  Only cells with\nvalue 1 may be traversed.  Returns -1 if no path exists.\n\nComplexity:\n    Time:  O(4^(M*N)) worst case (backtracking)\n    Space: O(M * N)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_path(maze: list[list[int]]) -> int:\n    \"\"\"Return the shortest path length in *maze*, or -1 if unreachable.\n\n    Args:\n        maze: 2D grid where 1 = passable, 0 = blocked.\n\n    Returns:\n        Minimum steps from (0,0) to (height-1, width-1), or -1.\n\n    Examples:\n        >>> find_path([[1, 1], [1, 1]])\n        2\n    \"\"\"\n    cnt = _dfs(maze, 0, 0, 0, -1)\n    return cnt\n\n\ndef _dfs(\n    maze: list[list[int]],\n    i: int,\n    j: int,\n    depth: int,\n    cnt: int,\n) -> int:\n    \"\"\"Recursive DFS helper for maze search.\n\n    Args:\n        maze: The grid (modified temporarily during recursion).\n        i: Current row.\n        j: Current column.\n        depth: Current path length.\n        cnt: Best path length found so far (-1 = none).\n\n    Returns:\n        Updated best path length.\n    \"\"\"\n    directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]\n\n    row = len(maze)\n    col = len(maze[0])\n\n    if i == row - 1 and j == col - 1:\n        if cnt == -1:\n            cnt = depth\n        else:\n            if cnt > depth:\n                cnt = depth\n        return cnt\n\n    maze[i][j] = 0\n\n    for k in range(len(directions)):\n        nx_i = i + directions[k][0]\n        nx_j = j + directions[k][1]\n\n        if 0 <= nx_i < row and 0 <= nx_j < col and maze[nx_i][nx_j] == 1:\n                cnt = _dfs(maze, nx_i, nx_j, depth + 1, cnt)\n\n    maze[i][j] = 1\n\n    return cnt\n"
  },
  {
    "path": "algorithms/graph/minimum_spanning_tree.py",
    "content": "\"\"\"\nMinimum Spanning Tree (Kruskal's Algorithm)\n\nFinds the MST of an undirected graph using Kruskal's algorithm with a\ndisjoint-set (union-find) data structure.\n\nReference: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm\n\nComplexity:\n    Time:  O(E log E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Edge:\n    \"\"\"An edge of an undirected weighted graph.\"\"\"\n\n    def __init__(self, source: int, target: int, weight: int) -> None:\n        self.source = source\n        self.target = target\n        self.weight = weight\n\n\nclass DisjointSet:\n    \"\"\"Union-Find data structure with path compression and union by size.\"\"\"\n\n    def __init__(self, size: int) -> None:\n        \"\"\"Create *size* singleton sets.\n\n        Args:\n            size: Number of elements.\n        \"\"\"\n        self.parent = list(range(size))\n        self.size = [1] * size\n\n    def merge_set(self, node1: int, node2: int) -> None:\n        \"\"\"Merge the sets containing *node1* and *node2*.\n\n        Args:\n            node1: First element.\n            node2: Second element.\n        \"\"\"\n        node1 = self.find_set(node1)\n        node2 = self.find_set(node2)\n\n        if self.size[node1] < self.size[node2]:\n            self.parent[node1] = node2\n            self.size[node2] += self.size[node1]\n        else:\n            self.parent[node2] = node1\n            self.size[node1] += self.size[node2]\n\n    def find_set(self, node: int) -> int:\n        \"\"\"Return the root representative of the set containing *node*.\n\n        Args:\n            node: Element to look up.\n\n        Returns:\n            Root representative.\n        \"\"\"\n        if self.parent[node] != node:\n            self.parent[node] = self.find_set(self.parent[node])\n        return self.parent[node]\n\n\ndef kruskal(vertex_count: int, edges: list[Edge], forest: DisjointSet) -> int:\n    \"\"\"Return the total weight of the MST computed by Kruskal's algorithm.\n\n    Args:\n        vertex_count: Number of vertices.\n        edges: List of weighted edges.\n        forest: Disjoint-set instance for the vertices.\n\n    Returns:\n        Sum of weights in the minimum spanning tree.\n\n    Examples:\n        >>> e = [Edge(0, 1, 1), Edge(1, 2, 2), Edge(0, 2, 3)]\n        >>> kruskal(3, e, DisjointSet(3))\n        3\n    \"\"\"\n    edges.sort(key=lambda edge: edge.weight)\n\n    mst: list[Edge] = []\n\n    for edge in edges:\n        set_u = forest.find_set(edge.source)\n        set_v = forest.find_set(edge.target)\n        if set_u != set_v:\n            forest.merge_set(set_u, set_v)\n            mst.append(edge)\n            if len(mst) == vertex_count - 1:\n                break\n\n    return sum(edge.weight for edge in mst)\n"
  },
  {
    "path": "algorithms/graph/pacific_atlantic.py",
    "content": "\"\"\"\nPacific Atlantic Water Flow\n\nGiven an m*n matrix of heights, find all cells from which water can flow\nto both the Pacific (top / left edges) and Atlantic (bottom / right edges)\noceans.\n\nReference: https://leetcode.com/problems/pacific-atlantic-water-flow/\n\nComplexity:\n    Time:  O(M * N)\n    Space: O(M * N)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef pacific_atlantic(matrix: list[list[int]]) -> list[list[int]]:\n    \"\"\"Return coordinates where water can flow to both oceans.\n\n    Args:\n        matrix: Height map.\n\n    Returns:\n        List of [row, col] pairs.\n\n    Examples:\n        >>> pacific_atlantic([[1]])\n        [[0, 0]]\n    \"\"\"\n    n = len(matrix)\n    if not n:\n        return []\n    m = len(matrix[0])\n    if not m:\n        return []\n    res: list[list[int]] = []\n    atlantic = [[False for _ in range(n)] for _ in range(m)]\n    pacific = [[False for _ in range(n)] for _ in range(m)]\n    for i in range(n):\n        _dfs(pacific, matrix, float(\"-inf\"), i, 0)\n        _dfs(atlantic, matrix, float(\"-inf\"), i, m - 1)\n    for i in range(m):\n        _dfs(pacific, matrix, float(\"-inf\"), 0, i)\n        _dfs(atlantic, matrix, float(\"-inf\"), n - 1, i)\n    for i in range(n):\n        for j in range(m):\n            if pacific[i][j] and atlantic[i][j]:\n                res.append([i, j])\n    return res\n\n\ndef _dfs(\n    grid: list[list[bool]],\n    matrix: list[list[int]],\n    height: float,\n    i: int,\n    j: int,\n) -> None:\n    \"\"\"Mark cells reachable from (i, j) flowing uphill.\n\n    Args:\n        grid: Reachability matrix (modified in place).\n        matrix: Height map.\n        height: Previous cell height.\n        i: Row index.\n        j: Column index.\n    \"\"\"\n    if i < 0 or i >= len(matrix) or j < 0 or j >= len(matrix[0]):\n        return\n    if grid[i][j] or matrix[i][j] < height:\n        return\n    grid[i][j] = True\n    _dfs(grid, matrix, matrix[i][j], i - 1, j)\n    _dfs(grid, matrix, matrix[i][j], i + 1, j)\n    _dfs(grid, matrix, matrix[i][j], i, j - 1)\n    _dfs(grid, matrix, matrix[i][j], i, j + 1)\n"
  },
  {
    "path": "algorithms/graph/path_between_two_vertices_in_digraph.py",
    "content": "\"\"\"\nPath Between Two Vertices in a Directed Graph\n\nDetermines whether there is a directed path from a source vertex to a\ntarget vertex using DFS.\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import defaultdict\n\n\nclass Graph:\n    \"\"\"A directed graph for reachability queries.\"\"\"\n\n    def __init__(self, vertex_count: int) -> None:\n        \"\"\"Create a graph with *vertex_count* vertices.\n\n        Args:\n            vertex_count: Number of vertices.\n        \"\"\"\n        self.vertex_count = vertex_count\n        self.graph: dict[int, list[int]] = defaultdict(list)\n        self.has_path = False\n\n    def add_edge(self, source: int, target: int) -> None:\n        \"\"\"Add a directed edge.\n\n        Args:\n            source: Source vertex.\n            target: Target vertex.\n        \"\"\"\n        self.graph[source].append(target)\n\n    def _dfs(self, source: int, target: int) -> None:\n        \"\"\"Run DFS to determine reachability.\n\n        Args:\n            source: Source vertex.\n            target: Target vertex.\n        \"\"\"\n        visited = [False] * self.vertex_count\n        self._dfs_util(visited, source, target)\n\n    def _dfs_util(self, visited: list[bool], source: int, target: int) -> None:\n        \"\"\"Recursive DFS helper.\n\n        Args:\n            visited: Visited flags.\n            source: Current vertex.\n            target: Destination vertex.\n        \"\"\"\n        visited[source] = True\n        for i in self.graph[source]:\n            if target in self.graph[source]:\n                self.has_path = True\n                return\n            if not visited[i]:\n                self._dfs_util(visited, source, i)\n\n    def is_reachable(self, source: int, target: int) -> bool:\n        \"\"\"Return True if *target* is reachable from *source*.\n\n        Args:\n            source: Source vertex.\n            target: Target vertex.\n\n        Returns:\n            True if a directed path exists.\n\n        Examples:\n            >>> g = Graph(2); g.add_edge(0, 1); g.is_reachable(0, 1)\n            True\n        \"\"\"\n        self.has_path = False\n        self._dfs(source, target)\n        return self.has_path\n"
  },
  {
    "path": "algorithms/graph/prims_minimum_spanning.py",
    "content": "\"\"\"\nPrim's Minimum Spanning Tree\n\nComputes the weight of a minimum spanning tree for a connected weighted\nundirected graph using a priority queue.\n\nReference: https://en.wikipedia.org/wiki/Prim%27s_algorithm\n\nComplexity:\n    Time:  O(E log V)\n    Space: O(V + E)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport heapq\nfrom typing import Any\n\n\ndef prims_minimum_spanning(\n    graph_used: dict[Any, list[list[int | Any]]],\n) -> int:\n    \"\"\"Return the total weight of the MST using Prim's algorithm.\n\n    Args:\n        graph_used: Adjacency list as ``{node: [[weight, neighbour], ...]}``.\n\n    Returns:\n        Sum of edge weights in the minimum spanning tree.\n\n    Examples:\n        >>> prims_minimum_spanning({1: [[1, 2]], 2: [[1, 1]]})\n        1\n    \"\"\"\n    vis: list[Any] = []\n    heap: list[list[int | Any]] = [[0, 1]]\n    prim: set[Any] = set()\n    mincost = 0\n\n    while len(heap) > 0:\n        cost, node = heapq.heappop(heap)\n        if node in vis:\n            continue\n\n        mincost += cost\n        prim.add(node)\n        vis.append(node)\n\n        for distance, adjacent in graph_used[node]:\n            if adjacent not in vis:\n                heapq.heappush(heap, [distance, adjacent])\n\n    return mincost\n"
  },
  {
    "path": "algorithms/graph/satisfiability.py",
    "content": "\"\"\"\n2-SAT Satisfiability\n\nGiven a formula in conjunctive normal form (2-CNF), finds an assignment of\nTrue/False values that satisfies all clauses, or reports that no solution\nexists.\n\nReference: https://en.wikipedia.org/wiki/2-satisfiability\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\n\ndef _dfs_transposed(\n    vertex: Any,\n    graph: dict[Any, list[Any]],\n    order: list[Any],\n    visited: dict[Any, bool],\n) -> None:\n    \"\"\"DFS on the transposed graph, recording finish order.\n\n    Args:\n        vertex: Current vertex.\n        graph: Transposed graph adjacency list.\n        order: Finish order (appended to).\n        visited: Visited flags.\n    \"\"\"\n    visited[vertex] = True\n    for adjacent in graph[vertex]:\n        if not visited[adjacent]:\n            _dfs_transposed(adjacent, graph, order, visited)\n    order.append(vertex)\n\n\ndef _dfs(\n    vertex: Any,\n    current_comp: int,\n    vertex_scc: dict[Any, int],\n    graph: dict[Any, list[Any]],\n    visited: dict[Any, bool],\n) -> None:\n    \"\"\"DFS assigning SCC labels.\n\n    Args:\n        vertex: Current vertex.\n        current_comp: Current component label.\n        vertex_scc: SCC mapping (modified in place).\n        graph: Graph adjacency list.\n        visited: Visited flags.\n    \"\"\"\n    visited[vertex] = True\n    vertex_scc[vertex] = current_comp\n    for adjacent in graph[vertex]:\n        if not visited[adjacent]:\n            _dfs(adjacent, current_comp, vertex_scc, graph, visited)\n\n\ndef _add_edge(graph: dict[Any, list[Any]], vertex_from: Any, vertex_to: Any) -> None:\n    \"\"\"Add a directed edge.\n\n    Args:\n        graph: Adjacency list (modified in place).\n        vertex_from: Source vertex.\n        vertex_to: Target vertex.\n    \"\"\"\n    if vertex_from not in graph:\n        graph[vertex_from] = []\n    graph[vertex_from].append(vertex_to)\n\n\ndef _scc(graph: dict[Any, list[Any]]) -> dict[Any, int]:\n    \"\"\"Compute SCCs using Kosaraju's algorithm.\n\n    Args:\n        graph: Directed graph adjacency list.\n\n    Returns:\n        Mapping from vertex to its SCC index.\n    \"\"\"\n    order: list[Any] = []\n    visited = {vertex: False for vertex in graph}\n\n    graph_transposed: dict[Any, list[Any]] = {vertex: [] for vertex in graph}\n\n    for source, neighbours in graph.items():\n        for target in neighbours:\n            _add_edge(graph_transposed, target, source)\n\n    for vertex in graph:\n        if not visited[vertex]:\n            _dfs_transposed(vertex, graph_transposed, order, visited)\n\n    visited = {vertex: False for vertex in graph}\n    vertex_scc: dict[Any, int] = {}\n\n    current_comp = 0\n    for vertex in reversed(order):\n        if not visited[vertex]:\n            _dfs(vertex, current_comp, vertex_scc, graph, visited)\n            current_comp += 1\n\n    return vertex_scc\n\n\ndef _build_graph(\n    formula: list[tuple[tuple[str, bool], tuple[str, bool]]],\n) -> dict[tuple[str, bool], list[tuple[str, bool]]]:\n    \"\"\"Build the implication graph from a 2-CNF formula.\n\n    Args:\n        formula: List of clauses, each a pair of literals\n            ``(name, is_negated)``.\n\n    Returns:\n        Implication graph as an adjacency list.\n    \"\"\"\n    graph: dict[tuple[str, bool], list[tuple[str, bool]]] = {}\n\n    for clause in formula:\n        for lit, _ in clause:\n            for neg in [False, True]:\n                graph[(lit, neg)] = []\n\n    for (a_lit, a_neg), (b_lit, b_neg) in formula:\n        _add_edge(graph, (a_lit, a_neg), (b_lit, not b_neg))\n        _add_edge(graph, (b_lit, b_neg), (a_lit, not a_neg))\n\n    return graph\n\n\ndef solve_sat(\n    formula: list[tuple[tuple[str, bool], tuple[str, bool]]],\n) -> dict[str, bool] | None:\n    \"\"\"Solve a 2-SAT formula.\n\n    Args:\n        formula: List of clauses in 2-CNF.\n\n    Returns:\n        A satisfying assignment as ``{variable: value}`` or None if\n        unsatisfiable.\n\n    Examples:\n        >>> solve_sat([(('x', False), ('y', False)), (('x', True), ('y', True))])\n        {'x': False, 'y': False}\n    \"\"\"\n    graph = _build_graph(formula)\n    vertex_scc = _scc(graph)\n\n    for var, _ in graph:\n        if vertex_scc[(var, False)] == vertex_scc[(var, True)]:\n            return None\n\n    comp_repr: dict[int, tuple[str, bool]] = {}\n\n    for vertex in graph:\n        if vertex_scc[vertex] not in comp_repr:\n            comp_repr[vertex_scc[vertex]] = vertex\n\n    comp_value: dict[int, bool] = {}\n    components = sorted(vertex_scc.values())\n\n    for comp in components:\n        if comp not in comp_value:\n            comp_value[comp] = False\n            lit, neg = comp_repr[comp]\n            comp_value[vertex_scc[(lit, not neg)]] = True\n\n    value = {var: comp_value[vertex_scc[(var, False)]] for var, _ in graph}\n\n    return value\n"
  },
  {
    "path": "algorithms/graph/shortest_distance_from_all_buildings.py",
    "content": "\"\"\"\nShortest Distance from All Buildings\n\nGiven a 2D grid with buildings (1), empty land (0) and obstacles (2), find\nthe empty land with the smallest total distance to all buildings.\n\nReference: https://leetcode.com/problems/shortest-distance-from-all-buildings/\n\nComplexity:\n    Time:  O(B * M * N)  where B is the number of buildings\n    Space: O(M * N)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\ndef shortest_distance(grid: list[list[int]]) -> int:\n    \"\"\"Return the minimum total distance from an empty cell to all buildings.\n\n    Args:\n        grid: 2D grid (0 = empty, 1 = building, 2 = obstacle).\n\n    Returns:\n        Minimum sum of distances, or -1 if impossible.\n\n    Examples:\n        >>> shortest_distance([[1, 0, 1]])\n        2\n    \"\"\"\n    if not grid or not grid[0]:\n        return -1\n\n    matrix = [[[0, 0] for _ in range(len(grid[0]))] for _ in range(len(grid))]\n\n    count = 0\n    for i in range(len(grid)):\n        for j in range(len(grid[0])):\n            if grid[i][j] == 1:\n                _bfs(grid, matrix, i, j, count)\n                count += 1\n\n    res = float(\"inf\")\n    for i in range(len(matrix)):\n        for j in range(len(matrix[0])):\n            if matrix[i][j][1] == count:\n                res = min(res, matrix[i][j][0])\n\n    return res if res != float(\"inf\") else -1\n\n\ndef _bfs(\n    grid: list[list[int]],\n    matrix: list[list[list[int]]],\n    i: int,\n    j: int,\n    count: int,\n) -> None:\n    \"\"\"BFS from building at (i, j), updating *matrix* distances.\n\n    Args:\n        grid: The original grid.\n        matrix: Accumulator for [total_distance, visit_count].\n        i: Row of the building.\n        j: Column of the building.\n        count: Number of buildings visited so far.\n    \"\"\"\n    q: deque[tuple[int, int, int]] = deque([(i, j, 0)])\n    while q:\n        i, j, step = q.popleft()\n        for k, col in [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]:\n            if (\n                0 <= k < len(grid)\n                and 0 <= col < len(grid[0])\n                and matrix[k][col][1] == count\n                and grid[k][col] == 0\n            ):\n                matrix[k][col][0] += step + 1\n                matrix[k][col][1] = count + 1\n                q.append((k, col, step + 1))\n"
  },
  {
    "path": "algorithms/graph/strongly_connected_components_kosaraju.py",
    "content": "\"\"\"\nStrongly Connected Components (Kosaraju's Algorithm)\n\nCounts the number of strongly connected components in a directed graph\nusing two DFS passes.\n\nReference: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V + E)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Kosaraju:\n    \"\"\"Kosaraju's algorithm for counting SCCs.\"\"\"\n\n    def dfs(\n        self,\n        i: int,\n        vertices: int,\n        adj: list[list[int]],\n        visited: list[int],\n        stk: list[int],\n    ) -> None:\n        \"\"\"DFS that records vertices in finish-time order.\n\n        Args:\n            i: Current vertex.\n            vertices: Number of vertices.\n            adj: Adjacency list.\n            visited: Visited flags (-1 = unvisited).\n            stk: Stack recording finish order.\n        \"\"\"\n        visited[i] = 1\n\n        for x in adj[i]:\n            if visited[x] == -1:\n                self.dfs(x, vertices, adj, visited, stk)\n\n        stk.append(i)\n\n    def kosaraju(self, vertices: int, adj: list[list[int]]) -> int:\n        \"\"\"Return the number of strongly connected components.\n\n        Args:\n            vertices: Number of vertices.\n            adj: Adjacency list.\n\n        Returns:\n            Count of SCCs.\n\n        Examples:\n            >>> Kosaraju().kosaraju(3, [[1], [2], [0]])\n            1\n        \"\"\"\n        stk: list[int] = []\n        visited = [-1] * (vertices + 1)\n\n        for i in range(vertices):\n            if visited[i] == -1:\n                self.dfs(i, vertices, adj, visited, stk)\n\n        stk.reverse()\n        res = stk.copy()\n\n        ans = 0\n        visited1 = [-1] * (vertices + 1)\n\n        adj1: list[list[int]] = [[] for _ in range(vertices)]\n\n        for i in range(len(adj)):\n            for x in adj[i]:\n                adj1[x].append(i)\n\n        for i in range(len(res)):\n            if visited1[res[i]] == -1:\n                ans += 1\n                self.dfs(res[i], vertices, adj1, visited1, stk)\n\n        return ans\n"
  },
  {
    "path": "algorithms/graph/sudoku_solver.py",
    "content": "\"\"\"\nSudoku Solver (DFS / Backtracking)\n\nSolves a Sudoku puzzle using constraint propagation and depth-first search\nwith backtracking, starting from the cell with the fewest possible values.\n\nReference: https://leetcode.com/problems/sudoku-solver/\n\nComplexity:\n    Time:  O(9^(empty cells)) worst case\n    Space: O(N^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Sudoku:\n    \"\"\"A Sudoku board solver.\"\"\"\n\n    def __init__(\n        self,\n        board: list[list[str]],\n        row: int,\n        col: int,\n    ) -> None:\n        \"\"\"Initialise the solver with the given board.\n\n        Args:\n            board: 2D list of digits or '.' for empty cells.\n            row: Number of rows.\n            col: Number of columns.\n        \"\"\"\n        self.board = board\n        self.row = row\n        self.col = col\n        self.val = self._possible_values()\n\n    def _possible_values(self) -> dict[tuple[int, int], list[str]]:\n        \"\"\"Compute possible values for each empty cell.\n\n        Returns:\n            Mapping from (row, col) to list of candidate digits.\n        \"\"\"\n        a = \"123456789\"\n        d: dict[tuple[str, int] | tuple[int, int], list[str]] = {}\n        val: dict[tuple[int, int], list[str]] = {}\n        for i in range(self.row):\n            for j in range(self.col):\n                ele = self.board[i][j]\n                if ele != \".\":\n                    d[(\"r\", i)] = d.get((\"r\", i), []) + [ele]\n                    d[(\"c\", j)] = d.get((\"c\", j), []) + [ele]\n                    d[(i // 3, j // 3)] = d.get((i // 3, j // 3), []) + [ele]\n                else:\n                    val[(i, j)] = []\n        for i, j in val:\n            inval = (\n                d.get((\"r\", i), []) + d.get((\"c\", j), []) + d.get((i / 3, j / 3), [])\n            )\n            val[(i, j)] = [n for n in a if n not in inval]\n        return val\n\n    def solve(self) -> bool:\n        \"\"\"Attempt to solve the board in place.\n\n        Returns:\n            True if a solution was found.\n        \"\"\"\n        if len(self.val) == 0:\n            return True\n        kee = min(self.val.keys(), key=lambda x: len(self.val[x]))\n        nums = self.val[kee]\n        for n in nums:\n            update: dict[tuple[int, int], str | list[str]] = {kee: self.val[kee]}\n            if self._valid_one(n, kee, update) and self.solve():\n                    return True\n            self._undo(kee, update)\n        return False\n\n    def _valid_one(\n        self,\n        n: str,\n        kee: tuple[int, int],\n        update: dict[tuple[int, int], str | list[str]],\n    ) -> bool:\n        \"\"\"Place digit *n* at *kee* and propagate constraints.\n\n        Args:\n            n: Digit to place.\n            kee: (row, col) coordinate.\n            update: Undo log (modified in place).\n\n        Returns:\n            True if placement is valid.\n        \"\"\"\n        self.board[kee[0]][kee[1]] = n\n        del self.val[kee]\n        i, j = kee\n        for ind in list(self.val.keys()):\n            if n in self.val[ind] and (\n                ind[0] == i\n                or ind[1] == j\n                or (ind[0] / 3, ind[1] / 3) == (i / 3, j / 3)\n            ):\n                    update[ind] = n\n                    self.val[ind].remove(n)\n                    if len(self.val[ind]) == 0:\n                        return False\n        return True\n\n    def _undo(\n        self,\n        kee: tuple[int, int],\n        update: dict[tuple[int, int], str | list[str]],\n    ) -> None:\n        \"\"\"Revert the placement at *kee* using *update*.\n\n        Args:\n            kee: (row, col) coordinate.\n            update: Undo log.\n        \"\"\"\n        self.board[kee[0]][kee[1]] = \".\"\n        for k in update:\n            if k not in self.val:\n                self.val[k] = update[k]\n            else:\n                self.val[k].append(update[k])\n\n    def __str__(self) -> str:\n        \"\"\"Return a string representation of the board.\n\n        Returns:\n            Formatted board string.\n        \"\"\"\n        resp = \"\"\n        for i in range(self.row):\n            for j in range(self.col):\n                resp += f\" {self.board[i][j]} \"\n            resp += \"\\n\"\n        return resp\n"
  },
  {
    "path": "algorithms/graph/tarjan.py",
    "content": "\"\"\"\nTarjan's Strongly Connected Components Algorithm\n\nFinds all strongly connected components in a directed graph.\n\nReference: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.graph.graph import DirectedGraph\n\n\nclass Tarjan:\n    \"\"\"Find SCCs via Tarjan's algorithm.\"\"\"\n\n    def __init__(self, dict_graph: dict[str, list[str]]) -> None:\n        \"\"\"Build the graph and compute all SCCs.\n\n        Args:\n            dict_graph: Adjacency dict ``{vertex: [neighbours]}``.\n        \"\"\"\n        self.graph = DirectedGraph(dict_graph)\n        self.index = 0\n        self.stack: list = []\n\n        for vertex in self.graph.nodes:\n            vertex.index = None\n\n        self.sccs: list[list] = []\n        for vertex in self.graph.nodes:\n            if vertex.index is None:\n                self._strongconnect(vertex, self.sccs)\n\n    def _strongconnect(self, vertex: object, sccs: list[list]) -> None:\n        \"\"\"Process *vertex* and discover its SCC.\n\n        Args:\n            vertex: Current vertex node.\n            sccs: Accumulated list of SCCs.\n        \"\"\"\n        vertex.index = self.index\n        vertex.lowlink = self.index\n        self.index += 1\n        self.stack.append(vertex)\n        vertex.on_stack = True\n\n        for adjacent in self.graph.adjacency_list[vertex]:\n            if adjacent.index is None:\n                self._strongconnect(adjacent, sccs)\n                vertex.lowlink = min(vertex.lowlink, adjacent.lowlink)\n            elif adjacent.on_stack:\n                vertex.lowlink = min(vertex.lowlink, adjacent.index)\n\n        if vertex.lowlink == vertex.index:\n            scc: list = []\n            while True:\n                adjacent = self.stack.pop()\n                adjacent.on_stack = False\n                scc.append(adjacent)\n                if adjacent == vertex:\n                    break\n            scc.sort()\n            sccs.append(scc)\n"
  },
  {
    "path": "algorithms/graph/topological_sort_bfs.py",
    "content": "\"\"\"\r\nTopological Sort (Kahn's Algorithm / BFS)\r\n\r\nComputes a topological ordering of a directed acyclic graph.  Raises\r\nValueError when a cycle is detected.\r\n\r\nReference: https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm\r\n\r\nComplexity:\r\n    Time:  O(V + E)\r\n    Space: O(V + E)\r\n\"\"\"\r\n\r\nfrom __future__ import annotations\r\n\r\nfrom collections import defaultdict, deque\r\n\r\n\r\ndef topological_sort(vertices: int, edges: list[tuple[int, int]]) -> list[int]:\r\n    \"\"\"Return a topological ordering of the vertices.\r\n\r\n    Args:\r\n        vertices: Number of vertices (labelled 0 .. vertices-1).\r\n        edges: Directed edges as (u, v) meaning u -> v.\r\n\r\n    Returns:\r\n        List of vertices in topological order.\r\n\r\n    Raises:\r\n        ValueError: If the graph contains a cycle.\r\n\r\n    Examples:\r\n        >>> topological_sort(3, [(0, 1), (1, 2)])\r\n        [0, 1, 2]\r\n    \"\"\"\r\n    graph: dict[int, list[int]] = defaultdict(list)\r\n\r\n    in_degree = [0] * vertices\r\n\r\n    for u, v in edges:\r\n        graph[u].append(v)\r\n        in_degree[v] += 1\r\n\r\n    queue: deque[int] = deque()\r\n    for i in range(vertices):\r\n        if in_degree[i] == 0:\r\n            queue.append(i)\r\n\r\n    sorted_order: list[int] = []\r\n    processed = 0\r\n\r\n    while queue:\r\n        node = queue.popleft()\r\n        sorted_order.append(node)\r\n        processed += 1\r\n\r\n        for neighbor in graph[node]:\r\n            in_degree[neighbor] -= 1\r\n            if in_degree[neighbor] == 0:\r\n                queue.append(neighbor)\r\n\r\n    if processed != vertices:\r\n        raise ValueError(\"Cycle detected, topological sort failed\")\r\n\r\n    return sorted_order\r\n"
  },
  {
    "path": "algorithms/graph/topological_sort_dfs.py",
    "content": "\"\"\"\nTopological Sort\n\nTopological sort produces a linear ordering of vertices in a directed\nacyclic graph (DAG) such that for every directed edge (u, v), vertex u\ncomes before v.  Two implementations are provided: one recursive\n(DFS-based) and one iterative.\n\nReference: https://en.wikipedia.org/wiki/Topological_sorting\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\n_GRAY, _BLACK = 0, 1\n\n\ndef top_sort_recursive(graph: dict[str, list[str]]) -> list[str]:\n    \"\"\"Return a topological ordering of *graph* using recursive DFS.\n\n    Args:\n        graph: Adjacency-list representation of a directed graph.\n\n    Returns:\n        A list of vertices in topological order.\n\n    Raises:\n        ValueError: If the graph contains a cycle.\n\n    Examples:\n        >>> top_sort_recursive({'a': ['b'], 'b': []})\n        ['b', 'a']\n    \"\"\"\n    order: list[str] = []\n    enter = set(graph)\n    state: dict[str, int] = {}\n\n    def _dfs(node: str) -> None:\n        state[node] = _GRAY\n        for neighbour in graph.get(node, ()):\n            neighbour_state = state.get(neighbour)\n            if neighbour_state == _GRAY:\n                raise ValueError(\"cycle\")\n            if neighbour_state == _BLACK:\n                continue\n            enter.discard(neighbour)\n            _dfs(neighbour)\n        order.append(node)\n        state[node] = _BLACK\n\n    while enter:\n        _dfs(enter.pop())\n    return order\n\n\ndef top_sort(graph: dict[str, list[str]]) -> list[str]:\n    \"\"\"Return a topological ordering of *graph* using an iterative approach.\n\n    Args:\n        graph: Adjacency-list representation of a directed graph.\n\n    Returns:\n        A list of vertices in topological order.\n\n    Raises:\n        ValueError: If the graph contains a cycle.\n\n    Examples:\n        >>> top_sort({'a': ['b'], 'b': []})\n        ['b', 'a']\n    \"\"\"\n    order: list[str] = []\n    enter = set(graph)\n    state: dict[str, int] = {}\n\n    def _is_ready(node: str) -> bool:\n        neighbours = graph.get(node, ())\n        if len(neighbours) == 0:\n            return True\n        for neighbour in neighbours:\n            neighbour_state = state.get(neighbour)\n            if neighbour_state == _GRAY:\n                raise ValueError(\"cycle\")\n            if neighbour_state != _BLACK:\n                return False\n        return True\n\n    while enter:\n        node = enter.pop()\n        stack: list[str] = []\n        while True:\n            state[node] = _GRAY\n            stack.append(node)\n            for neighbour in graph.get(node, ()):\n                neighbour_state = state.get(neighbour)\n                if neighbour_state == _GRAY:\n                    raise ValueError(\"cycle\")\n                if neighbour_state == _BLACK:\n                    continue\n                enter.discard(neighbour)\n                stack.append(neighbour)\n            while stack and _is_ready(stack[-1]):\n                node = stack.pop()\n                order.append(node)\n                state[node] = _BLACK\n            if len(stack) == 0:\n                break\n            node = stack.pop()\n\n    return order\n"
  },
  {
    "path": "algorithms/graph/transitive_closure_dfs.py",
    "content": "\"\"\"\nTransitive Closure via DFS\n\nComputes the transitive closure of a directed graph using depth-first\nsearch.\n\nReference: https://en.wikipedia.org/wiki/Transitive_closure#In_graph_theory\n\nComplexity:\n    Time:  O(V * (V + E))\n    Space: O(V^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Graph:\n    \"\"\"A directed graph for transitive closure computation.\"\"\"\n\n    def __init__(self, vertices: int) -> None:\n        \"\"\"Create a graph with *vertices* vertices.\n\n        Args:\n            vertices: Number of vertices.\n        \"\"\"\n        self.vertex_count = vertices\n        self.graph: dict[int, list[int]] = {}\n        self.closure = [[0 for _ in range(vertices)] for _ in range(vertices)]\n\n    def add_edge(self, source: int, target: int) -> None:\n        \"\"\"Add a directed edge.\n\n        Args:\n            source: Source vertex.\n            target: Target vertex.\n        \"\"\"\n        if source in self.graph:\n            self.graph[source].append(target)\n        else:\n            self.graph[source] = [target]\n\n    def _dfs_util(self, source: int, target: int) -> None:\n        \"\"\"Recursive DFS marking reachability from *source* through *target*.\n\n        Args:\n            source: Origin vertex.\n            target: Current vertex being explored.\n        \"\"\"\n        self.closure[source][target] = 1\n\n        for adjacent in self.graph[target]:\n            if self.closure[source][adjacent] == 0:\n                self._dfs_util(source, adjacent)\n\n    def transitive_closure(self) -> list[list[int]]:\n        \"\"\"Compute and return the transitive closure matrix.\n\n        Returns:\n            An n*n matrix where entry [i][j] is 1 if j is reachable from i.\n\n        Examples:\n            >>> g = Graph(2); g.add_edge(0, 1); g.transitive_closure()\n            [[1, 1], [0, 1]]\n        \"\"\"\n        for i in range(self.vertex_count):\n            self._dfs_util(i, i)\n\n        return self.closure\n"
  },
  {
    "path": "algorithms/graph/traversal.py",
    "content": "\"\"\"\nGraph Traversal Algorithms\n\nProvides DFS and BFS traversal of a graph represented as an adjacency\ndictionary.\n\nComplexity:\n    Time:  O(V + E)\n    Space: O(V)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\nfrom typing import Any\n\n\ndef dfs_traverse(graph: dict[Any, list[Any]], start: Any) -> set[Any]:\n    \"\"\"Traverse the graph from *start* using iterative DFS.\n\n    Args:\n        graph: Adjacency list.\n        start: Starting node.\n\n    Returns:\n        Set of visited nodes.\n\n    Examples:\n        >>> sorted(dfs_traverse({'a': ['b'], 'b': []}, 'a'))\n        ['a', 'b']\n    \"\"\"\n    visited: set[Any] = set()\n    stack = [start]\n    while stack:\n        node = stack.pop()\n        if node not in visited:\n            visited.add(node)\n            for next_node in graph[node]:\n                if next_node not in visited:\n                    stack.append(next_node)\n    return visited\n\n\ndef bfs_traverse(graph: dict[Any, list[Any]], start: Any) -> set[Any]:\n    \"\"\"Traverse the graph from *start* using BFS.\n\n    Args:\n        graph: Adjacency list.\n        start: Starting node.\n\n    Returns:\n        Set of visited nodes.\n\n    Examples:\n        >>> sorted(bfs_traverse({'a': ['b'], 'b': []}, 'a'))\n        ['a', 'b']\n    \"\"\"\n    visited: set[Any] = set()\n    queue = deque([start])\n    while queue:\n        node = queue.popleft()\n        if node not in visited:\n            visited.add(node)\n            for next_node in graph[node]:\n                if next_node not in visited:\n                    queue.append(next_node)\n    return visited\n\n\ndef dfs_traverse_recursive(\n    graph: dict[Any, list[Any]],\n    start: Any,\n    visited: set[Any] | None = None,\n) -> set[Any]:\n    \"\"\"Traverse the graph from *start* using recursive DFS.\n\n    Args:\n        graph: Adjacency list.\n        start: Starting node.\n        visited: Already-visited set (internal use).\n\n    Returns:\n        Set of visited nodes.\n\n    Examples:\n        >>> sorted(dfs_traverse_recursive({'a': ['b'], 'b': []}, 'a'))\n        ['a', 'b']\n    \"\"\"\n    if visited is None:\n        visited = set()\n    visited.add(start)\n    for next_node in graph[start]:\n        if next_node not in visited:\n            dfs_traverse_recursive(graph, next_node, visited)\n    return visited\n"
  },
  {
    "path": "algorithms/graph/walls_and_gates.py",
    "content": "\"\"\"\nWalls and Gates\n\nFill each empty room (INF) with the distance to its nearest gate (0).\nWalls are represented by -1.\n\nReference: https://leetcode.com/problems/walls-and-gates/\n\nComplexity:\n    Time:  O(M * N)\n    Space: O(M * N) recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef walls_and_gates(rooms: list[list[int]]) -> None:\n    \"\"\"Fill *rooms* in place with distances to nearest gates.\n\n    Args:\n        rooms: 2D grid (-1 = wall, 0 = gate, INF = empty room).\n\n    Examples:\n        >>> r = [[float('inf'), 0]]; walls_and_gates(r); r\n        [[1, 0]]\n    \"\"\"\n    for i in range(len(rooms)):\n        for j in range(len(rooms[0])):\n            if rooms[i][j] == 0:\n                _dfs(rooms, i, j, 0)\n\n\ndef _dfs(rooms: list[list[int]], i: int, j: int, depth: int) -> None:\n    \"\"\"Recursive DFS from a gate, updating room distances.\n\n    Args:\n        rooms: The grid (modified in place).\n        i: Row index.\n        j: Column index.\n        depth: Current distance from the gate.\n    \"\"\"\n    if i < 0 or i >= len(rooms) or j < 0 or j >= len(rooms[0]):\n        return\n    if rooms[i][j] < depth:\n        return\n    rooms[i][j] = depth\n    _dfs(rooms, i + 1, j, depth + 1)\n    _dfs(rooms, i - 1, j, depth + 1)\n    _dfs(rooms, i, j + 1, depth + 1)\n    _dfs(rooms, i, j - 1, depth + 1)\n"
  },
  {
    "path": "algorithms/graph/word_ladder.py",
    "content": "\"\"\"\nWord Ladder (Bidirectional BFS)\n\nGiven two words and a dictionary, find the length of the shortest\ntransformation sequence where only one letter changes at each step and\nevery intermediate word must exist in the dictionary.\n\nReference: https://leetcode.com/problems/word-ladder/\n\nComplexity:\n    Time:  O(N * L^2)  where N = size of word list, L = word length\n    Space: O(N * L)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Iterator\n\n\ndef ladder_length(begin_word: str, end_word: str, word_list: list[str]) -> int:\n    \"\"\"Return the shortest transformation length, or -1 if impossible.\n\n    Args:\n        begin_word: Starting word.\n        end_word: Target word.\n        word_list: Allowed intermediate words.\n\n    Returns:\n        Length of the shortest transformation sequence, or -1.\n\n    Examples:\n        >>> ladder_length('hit', 'cog', ['hot', 'dot', 'dog', 'lot', 'log'])\n        5\n    \"\"\"\n    if len(begin_word) != len(end_word):\n        return -1\n\n    if begin_word == end_word:\n        return 0\n\n    if sum(c1 != c2 for c1, c2 in zip(begin_word, end_word, strict=False)) == 1:\n        return 1\n\n    begin_set: set[str] = set()\n    end_set: set[str] = set()\n    begin_set.add(begin_word)\n    end_set.add(end_word)\n    result = 2\n    while begin_set and end_set:\n        if len(begin_set) > len(end_set):\n            begin_set, end_set = end_set, begin_set\n\n        next_begin_set: set[str] = set()\n        for word in begin_set:\n            for ladder_word in _word_range(word):\n                if ladder_word in end_set:\n                    return result\n                if ladder_word in word_list:\n                    next_begin_set.add(ladder_word)\n                    word_list.remove(ladder_word)\n        begin_set = next_begin_set\n        result += 1\n    return -1\n\n\ndef _word_range(word: str) -> Iterator[str]:\n    \"\"\"Yield all words that differ from *word* by exactly one letter.\n\n    Args:\n        word: The source word.\n\n    Yields:\n        Words with a single character changed.\n    \"\"\"\n    for ind in range(len(word)):\n        temp = word[ind]\n        for c in [chr(x) for x in range(ord(\"a\"), ord(\"z\") + 1)]:\n            if c != temp:\n                yield word[:ind] + c + word[ind + 1 :]\n"
  },
  {
    "path": "algorithms/greedy/__init__.py",
    "content": "from .gale_shapley import gale_shapley\nfrom .max_contiguous_subsequence_sum import max_contiguous_subsequence_sum\n\n__all__ = [\n    \"gale_shapley\",\n    \"max_contiguous_subsequence_sum\",\n]\n"
  },
  {
    "path": "algorithms/greedy/gale_shapley.py",
    "content": "\"\"\"\nGale-Shapley Stable Matching\n\nSolves the stable matching (stable marriage) problem. Given N men and N women\nwith ranked preferences, produces a stable matching where no pair would prefer\neach other over their current partners.\n\nReference: https://en.wikipedia.org/wiki/Gale%E2%80%93Shapley_algorithm\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef gale_shapley(\n    men: dict[str, list[str]],\n    women: dict[str, list[str]],\n) -> dict[str, str]:\n    \"\"\"Find a stable matching between men and women.\n\n    Args:\n        men: Mapping of each man to his preference list of women\n            (highest to lowest).\n        women: Mapping of each woman to her preference list of men\n            (highest to lowest).\n\n    Returns:\n        A dict mapping each man to his matched woman.\n\n    Examples:\n        >>> men = {\"M1\": [\"W1\", \"W2\"], \"M2\": [\"W1\", \"W2\"]}\n        >>> women = {\"W1\": [\"M2\", \"M1\"], \"W2\": [\"M1\", \"M2\"]}\n        >>> sorted(gale_shapley(men, women).items())\n        [('M1', 'W2'), ('M2', 'W1')]\n    \"\"\"\n    men_available: list[str] = list(men.keys())\n    married: dict[str, str] = {}\n    proposal_counts: dict[str, int] = {man: 0 for man in men}\n\n    while men_available:\n        man = men_available.pop(0)\n        woman = men[man][proposal_counts[man]]\n        proposal_counts[man] += 1\n\n        if woman not in married:\n            married[woman] = man\n        else:\n            current_partner = married[woman]\n            if women[woman].index(man) < women[woman].index(current_partner):\n                married[woman] = man\n                men_available.append(current_partner)\n            else:\n                men_available.append(man)\n\n    return {man: woman for woman, man in married.items()}\n"
  },
  {
    "path": "algorithms/greedy/max_contiguous_subsequence_sum.py",
    "content": "\"\"\"\nMaximum Contiguous Subsequence Sum (Kadane's Algorithm)\n\nFinds the maximum sum of a contiguous sub-array within a one-dimensional\narray of numbers.  The algorithm is greedy / dynamic-programming hybrid.\n\nReference: https://en.wikipedia.org/wiki/Maximum_subarray_problem\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_contiguous_subsequence_sum(arr: list[int]) -> int:\n    \"\"\"Return the maximum contiguous subsequence sum.\n\n    The contiguous subsequence must contain at least one element.\n    Returns 0 for an empty array.\n\n    Args:\n        arr: A list of integers.\n\n    Returns:\n        The maximum contiguous subsequence sum.\n\n    Examples:\n        >>> max_contiguous_subsequence_sum([-2, 3, 8, -1, 4])\n        14\n        >>> max_contiguous_subsequence_sum([-1, -3, -4])\n        -1\n        >>> max_contiguous_subsequence_sum([])\n        0\n    \"\"\"\n    if not arr:\n        return 0\n\n    max_sum = arr[0]\n    current_sum = 0\n\n    for value in arr:\n        if current_sum + value < value:\n            current_sum = value\n        else:\n            current_sum += value\n        max_sum = max(max_sum, current_sum)\n\n    return max_sum\n"
  },
  {
    "path": "algorithms/heap/__init__.py",
    "content": "\"\"\"Heap-based algorithm implementations.\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.heap import AbstractHeap, BinaryHeap\n\nfrom .k_closest_points import k_closest\nfrom .merge_sorted_k_lists import ListNode, merge_k_lists\nfrom .skyline import get_skyline\nfrom .sliding_window_max import max_sliding_window\n\n__all__ = [\n    \"AbstractHeap\",\n    \"BinaryHeap\",\n    \"ListNode\",\n    \"get_skyline\",\n    \"k_closest\",\n    \"max_sliding_window\",\n    \"merge_k_lists\",\n]\n"
  },
  {
    "path": "algorithms/heap/k_closest_points.py",
    "content": "\"\"\"\nK Closest Points to Origin\n\nGiven a list of points, find the k closest to the origin using a max\nheap of size k. For each subsequent point, replace the heap root if\nthe new point is closer.\n\nReference: https://leetcode.com/problems/k-closest-points-to-origin/\n\nComplexity:\n    Time:  O(k + (n - k) log k)\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom heapq import heapify, heappushpop\n\n\ndef k_closest(\n    points: list[tuple[int, int]],\n    k: int,\n    origin: tuple[int, int] = (0, 0),\n) -> list[tuple[int, int]]:\n    \"\"\"Find the k closest points to the origin.\n\n    Args:\n        points: List of (x, y) coordinate tuples.\n        k: Number of closest points to return.\n        origin: The reference point, defaults to (0, 0).\n\n    Returns:\n        List of the k closest points.\n\n    Examples:\n        >>> k_closest([(1, 0), (-1, 0), (2, 3)], 2)\n        [(-1, 0), (1, 0)]\n    \"\"\"\n    heap = [(-_distance(p, origin), p) for p in points[:k]]\n    heapify(heap)\n\n    for point in points[k:]:\n        dist = _distance(point, origin)\n        heappushpop(heap, (-dist, point))\n\n    return [point for _, point in heap]\n\n\ndef _distance(point: tuple[int, int], origin: tuple[int, int] = (0, 0)) -> int:\n    \"\"\"Compute squared Euclidean distance from point to origin.\n\n    Args:\n        point: The (x, y) coordinate.\n        origin: The reference point.\n\n    Returns:\n        Squared Euclidean distance.\n    \"\"\"\n    return (point[0] - origin[0]) ** 2 + (point[1] - origin[1]) ** 2\n"
  },
  {
    "path": "algorithms/heap/merge_sorted_k_lists.py",
    "content": "\"\"\"\nMerge K Sorted Linked Lists\n\nMerge k sorted linked lists into one sorted linked list using a heap\nfor efficient minimum extraction.\n\nReference: https://leetcode.com/problems/merge-k-sorted-lists/\n\nComplexity:\n    Time:  O(n log k) where n is total elements and k is number of lists\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom heapq import heapify, heappop, heapreplace\n\n\nclass ListNode:\n    \"\"\"Singly linked list node.\n\n    Args:\n        val: The node value.\n    \"\"\"\n\n    def __init__(self, val: int) -> None:\n        self.val = val\n        self.next: ListNode | None = None\n\n\ndef merge_k_lists(lists: list[ListNode | None]) -> ListNode | None:\n    \"\"\"Merge k sorted linked lists into a single sorted linked list.\n\n    Args:\n        lists: A list of head nodes of sorted linked lists.\n\n    Returns:\n        Head of the merged sorted linked list, or None if all are empty.\n\n    Examples:\n        >>> n1 = ListNode(1)\n        >>> n2 = ListNode(2)\n        >>> result = merge_k_lists([n1, n2])\n        >>> result.val\n        1\n    \"\"\"\n    dummy = node = ListNode(0)\n    heap: list[tuple[int, int, ListNode]] = []\n    for idx, head in enumerate(lists):\n        if head:\n            heap.append((head.val, idx, head))\n    heapify(heap)\n    while heap:\n        val, idx, n_val = heap[0]\n        if n_val.next is None:\n            heappop(heap)\n        else:\n            heapreplace(heap, (n_val.next.val, idx, n_val.next))\n        node.next = n_val\n        node = node.next\n\n    return dummy.next\n"
  },
  {
    "path": "algorithms/heap/skyline.py",
    "content": "\"\"\"\nSkyline Problem\n\nGiven building triplets [left, right, height], compute the skyline\ncontour as a list of key points using a heap-based sweep line approach.\n\nReference: https://leetcode.com/problems/the-skyline-problem/\n\nComplexity:\n    Time:  O(n log n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport heapq\n\n\ndef get_skyline(lrh: list[list[int]]) -> list[list[int]]:\n    \"\"\"Compute the skyline from a list of buildings.\n\n    Args:\n        lrh: List of [left, right, height] building triplets, sorted\n            by left coordinate.\n\n    Returns:\n        List of [x, height] key points defining the skyline.\n\n    Examples:\n        >>> get_skyline([[2, 9, 10], [3, 7, 15]])\n        [[2, 10], [3, 15], [7, 10], [9, 0]]\n    \"\"\"\n    skyline: list[list[int]] = []\n    live: list[list[int]] = []\n    i, n = 0, len(lrh)\n    while i < n or live:\n        if not live or i < n and lrh[i][0] <= -live[0][1]:\n            x = lrh[i][0]\n            while i < n and lrh[i][0] == x:\n                heapq.heappush(live, [-lrh[i][2], -lrh[i][1]])\n                i += 1\n        else:\n            x = -live[0][1]\n            while live and -live[0][1] <= x:\n                heapq.heappop(live)\n        height = len(live) and -live[0][0]\n        if not skyline or height != skyline[-1][1]:\n            skyline += [[x, height]]\n    return skyline\n"
  },
  {
    "path": "algorithms/heap/sliding_window_max.py",
    "content": "\"\"\"\nSliding Window Maximum (Heap-based)\n\nGiven an array and a window size k, find the maximum element in each\nsliding window using a deque that maintains decreasing order of values.\n\nReference: https://leetcode.com/problems/sliding-window-maximum/\n\nComplexity:\n    Time:  O(n)\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef max_sliding_window(nums: list[int], k: int) -> list[int]:\n    \"\"\"Find the maximum in each sliding window of size k.\n\n    Args:\n        nums: Input array of integers.\n        k: Window size.\n\n    Returns:\n        List of maximum values for each window position.\n\n    Examples:\n        >>> max_sliding_window([1, 3, -1, -3, 5, 3, 6, 7], 3)\n        [3, 3, 5, 5, 6, 7]\n    \"\"\"\n    if not nums:\n        return nums\n    queue: collections.deque[int] = collections.deque()\n    result: list[int] = []\n    for num in nums:\n        if len(queue) < k:\n            queue.append(num)\n        else:\n            result.append(max(queue))\n            queue.popleft()\n            queue.append(num)\n    result.append(max(queue))\n    return result\n"
  },
  {
    "path": "algorithms/linked_list/__init__.py",
    "content": "\"\"\"Linked list algorithm implementations.\"\"\"\n\nfrom algorithms.data_structures.linked_list import (\n    DoublyLinkedListNode,\n    SinglyLinkedListNode,\n)\nfrom algorithms.linked_list.add_two_numbers import (\n    add_two_numbers,\n    convert_to_list,\n    convert_to_str,\n)\nfrom algorithms.linked_list.copy_random_pointer import (\n    RandomListNode,\n    copy_random_pointer_v1,\n    copy_random_pointer_v2,\n)\nfrom algorithms.linked_list.delete_node import delete_node\nfrom algorithms.linked_list.first_cyclic_node import first_cyclic_node\nfrom algorithms.linked_list.intersection import intersection\nfrom algorithms.linked_list.is_cyclic import is_cyclic\nfrom algorithms.linked_list.is_palindrome import (\n    is_palindrome,\n    is_palindrome_dict,\n    is_palindrome_stack,\n)\nfrom algorithms.linked_list.is_sorted import is_sorted\nfrom algorithms.linked_list.kth_to_last import (\n    kth_to_last,\n    kth_to_last_dict,\n    kth_to_last_eval,\n)\nfrom algorithms.linked_list.merge_two_list import (\n    merge_two_list,\n    merge_two_list_recur,\n)\nfrom algorithms.linked_list.partition import partition\nfrom algorithms.linked_list.remove_duplicates import (\n    remove_dups,\n    remove_dups_wothout_set,\n)\nfrom algorithms.linked_list.remove_range import remove_range\nfrom algorithms.linked_list.reverse import (\n    reverse_list,\n    reverse_list_recursive,\n)\nfrom algorithms.linked_list.rotate_list import rotate_right\nfrom algorithms.linked_list.swap_in_pairs import swap_pairs\n\n__all__ = [\n    \"add_two_numbers\",\n    \"convert_to_list\",\n    \"convert_to_str\",\n    \"RandomListNode\",\n    \"copy_random_pointer_v1\",\n    \"copy_random_pointer_v2\",\n    \"delete_node\",\n    \"first_cyclic_node\",\n    \"intersection\",\n    \"is_cyclic\",\n    \"is_palindrome\",\n    \"is_palindrome_dict\",\n    \"is_palindrome_stack\",\n    \"is_sorted\",\n    \"kth_to_last\",\n    \"kth_to_last_dict\",\n    \"kth_to_last_eval\",\n    \"DoublyLinkedListNode\",\n    \"SinglyLinkedListNode\",\n    \"merge_two_list\",\n    \"merge_two_list_recur\",\n    \"partition\",\n    \"remove_dups\",\n    \"remove_dups_wothout_set\",\n    \"remove_range\",\n    \"reverse_list\",\n    \"reverse_list_recursive\",\n    \"rotate_right\",\n    \"swap_pairs\",\n]\n"
  },
  {
    "path": "algorithms/linked_list/add_two_numbers.py",
    "content": "\"\"\"\nAdd Two Numbers (Linked List)\n\nGiven two non-empty linked lists representing two non-negative integers with\ndigits stored in reverse order, add the two numbers and return the sum as a\nlinked list.\n\nReference: https://leetcode.com/problems/add-two-numbers/\n\nComplexity:\n    Time:  O(max(m, n))\n    Space: O(max(m, n))\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: int) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef add_two_numbers(left: Node, right: Node) -> Node:\n    \"\"\"Add two numbers represented as reversed linked lists.\n\n    Args:\n        left: Head of the first number's linked list.\n        right: Head of the second number's linked list.\n\n    Returns:\n        Head of the resulting sum linked list.\n\n    Examples:\n        >>> # (2 -> 4 -> 3) + (5 -> 6 -> 4) = (7 -> 0 -> 8)\n        >>> l1 = Node(2); l1.next = Node(4); l1.next.next = Node(3)\n        >>> l2 = Node(5); l2.next = Node(6); l2.next.next = Node(4)\n        >>> convert_to_str(add_two_numbers(l1, l2))\n        '708'\n    \"\"\"\n    head = Node(0)\n    current = head\n    carry = 0\n    while left or right:\n        carry //= 10\n        if left:\n            carry += left.val\n            left = left.next\n        if right:\n            carry += right.val\n            right = right.next\n        current.next = Node(carry % 10)\n        current = current.next\n    if carry // 10 == 1:\n        current.next = Node(1)\n    return head.next\n\n\ndef convert_to_list(number: int) -> Node | None:\n    \"\"\"Convert a non-negative integer into a reversed linked list.\n\n    Args:\n        number: A non-negative integer to convert.\n\n    Returns:\n        Head of the reversed linked list, or None if number is negative.\n\n    Examples:\n        >>> convert_to_str(convert_to_list(112))\n        '211'\n    \"\"\"\n    if number < 0:\n        return None\n    head = Node(0)\n    current = head\n    remainder = number % 10\n    quotient = number // 10\n\n    while quotient != 0:\n        current.next = Node(remainder)\n        current = current.next\n        remainder = quotient % 10\n        quotient //= 10\n    current.next = Node(remainder)\n    return head.next\n\n\ndef convert_to_str(node: Node | None) -> str:\n    \"\"\"Convert a linked list of digits to a string.\n\n    Args:\n        node: Head of the linked list.\n\n    Returns:\n        String representation of the linked list values.\n\n    Examples:\n        >>> n = Node(2); n.next = Node(4); n.next.next = Node(3)\n        >>> convert_to_str(n)\n        '243'\n    \"\"\"\n    result = \"\"\n    while node:\n        result += str(node.val)\n        node = node.next\n    return result\n"
  },
  {
    "path": "algorithms/linked_list/copy_random_pointer.py",
    "content": "\"\"\"\nCopy List with Random Pointer\n\nGiven a linked list where each node contains an additional random pointer that\ncould point to any node in the list or null, return a deep copy of the list.\n\nReference: https://leetcode.com/problems/copy-list-with-random-pointer/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import defaultdict\n\n\nclass RandomListNode:\n    \"\"\"Node with next and random pointers for deep-copy problem.\"\"\"\n\n    def __init__(self, label: int) -> None:\n        self.label = label\n        self.next: RandomListNode | None = None\n        self.random: RandomListNode | None = None\n\n\ndef copy_random_pointer_v1(head: RandomListNode | None) -> RandomListNode | None:\n    \"\"\"Deep-copy a linked list with random pointers using a dictionary.\n\n    Args:\n        head: Head of the original list.\n\n    Returns:\n        Head of the deep-copied list.\n\n    Examples:\n        >>> node = RandomListNode(1)\n        >>> node.random = node\n        >>> copied = copy_random_pointer_v1(node)\n        >>> copied.label == 1 and copied.random is copied\n        True\n    \"\"\"\n    node_map: dict[RandomListNode, RandomListNode] = {}\n    current = head\n    while current:\n        node_map[current] = RandomListNode(current.label)\n        current = current.next\n    current = head\n    while current:\n        node_map[current].next = node_map.get(current.next)\n        node_map[current].random = node_map.get(current.random)\n        current = current.next\n    return node_map.get(head)\n\n\ndef copy_random_pointer_v2(head: RandomListNode | None) -> RandomListNode | None:\n    \"\"\"Deep-copy a linked list with random pointers using defaultdict.\n\n    Args:\n        head: Head of the original list.\n\n    Returns:\n        Head of the deep-copied list.\n\n    Examples:\n        >>> node = RandomListNode(1)\n        >>> node.random = node\n        >>> copied = copy_random_pointer_v2(node)\n        >>> copied.label == 1 and copied.random is copied\n        True\n    \"\"\"\n    copy: defaultdict[RandomListNode | None, RandomListNode | None] = defaultdict(\n        lambda: RandomListNode(0)\n    )\n    copy[None] = None\n    node = head\n    while node:\n        copy[node].label = node.label\n        copy[node].next = copy[node.next]\n        copy[node].random = copy[node.random]\n        node = node.next\n    return copy[head]\n"
  },
  {
    "path": "algorithms/linked_list/delete_node.py",
    "content": "\"\"\"\nDelete Node in a Linked List\n\nGiven only access to a node (not the tail) in a singly linked list, delete\nthat node by copying the next node's value and skipping over it.\n\nReference: https://leetcode.com/problems/delete-node-in-a-linked-list/\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: int) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef delete_node(node: Node | None) -> None:\n    \"\"\"Delete the given node from a singly linked list in-place.\n\n    The node must not be the tail node. The deletion is performed by copying\n    the value from the next node and then skipping the next node.\n\n    Args:\n        node: The node to delete (must not be None or the tail).\n\n    Raises:\n        ValueError: If node is None or is the tail node.\n\n    Examples:\n        >>> head = Node(1); head.next = Node(2); head.next.next = Node(3)\n        >>> delete_node(head.next)\n        >>> head.next.val\n        3\n    \"\"\"\n    if node is None or node.next is None:\n        raise ValueError\n    node.val = node.next.val\n    node.next = node.next.next\n"
  },
  {
    "path": "algorithms/linked_list/first_cyclic_node.py",
    "content": "\"\"\"\nFirst Cyclic Node\n\nGiven a linked list, find the first node of a cycle in it using Floyd's\ncycle-finding algorithm (Tortoise and Hare).\n\nReference: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: object) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef first_cyclic_node(head: Node | None) -> Node | None:\n    \"\"\"Find the first node of a cycle in the linked list.\n\n    Args:\n        head: Head of the linked list.\n\n    Returns:\n        The first node in the cycle, or None if there is no cycle.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); c = Node(3)\n        >>> a.next = b; b.next = c; c.next = b\n        >>> first_cyclic_node(a).val\n        2\n    \"\"\"\n    runner = walker = head\n    while runner and runner.next:\n        runner = runner.next.next\n        walker = walker.next\n        if runner is walker:\n            break\n\n    if runner is None or runner.next is None:\n        return None\n\n    walker = head\n    while runner is not walker:\n        runner, walker = runner.next, walker.next\n    return runner\n"
  },
  {
    "path": "algorithms/linked_list/intersection.py",
    "content": "\"\"\"\nIntersection of Two Linked Lists\n\nGiven two singly linked lists that converge at some node, find and return the\nintersecting node. The node identity (not value) is the unique identifier.\n\nReference: https://leetcode.com/problems/intersection-of-two-linked-lists/\n\nComplexity:\n    Time:  O(m + n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, val: object = None) -> None:\n        self.val = val\n        self.next: Node | None = None\n\n\ndef intersection(h1: Node, h2: Node) -> Node | None:\n    \"\"\"Find the intersection node of two linked lists.\n\n    Args:\n        h1: Head of the first linked list.\n        h2: Head of the second linked list.\n\n    Returns:\n        The intersecting node, or None if the lists do not intersect.\n\n    Examples:\n        >>> shared = Node(7)\n        >>> a = Node(1); a.next = shared\n        >>> b = Node(2); b.next = shared\n        >>> intersection(a, b).val\n        7\n    \"\"\"\n    count = 0\n    flag = None\n    h1_orig = h1\n    h2_orig = h2\n\n    while h1 or h2:\n        count += 1\n\n        if not flag and (h1.next is None or h2.next is None):\n            flag = (count, h1.next, h2.next)\n\n        if h1:\n            h1 = h1.next\n        if h2:\n            h2 = h2.next\n\n    long_len = count\n    short_len = flag[0]\n\n    if flag[1] is None:\n        shorter = h1_orig\n        longer = h2_orig\n    elif flag[2] is None:\n        shorter = h2_orig\n        longer = h1_orig\n\n    while longer and shorter:\n        while long_len > short_len:\n            longer = longer.next\n            long_len -= 1\n\n        if longer == shorter:\n            return longer\n        else:\n            longer = longer.next\n            shorter = shorter.next\n\n    return None\n"
  },
  {
    "path": "algorithms/linked_list/is_cyclic.py",
    "content": "\"\"\"\nLinked List Cycle Detection\n\nGiven a linked list, determine if it has a cycle using Floyd's Tortoise and\nHare algorithm without extra space.\n\nReference: https://leetcode.com/problems/linked-list-cycle/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: object) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef is_cyclic(head: Node | None) -> bool:\n    \"\"\"Determine whether a linked list contains a cycle.\n\n    Args:\n        head: Head of the linked list.\n\n    Returns:\n        True if the list has a cycle, False otherwise.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); a.next = b; b.next = a\n        >>> is_cyclic(a)\n        True\n        >>> c = Node(3); c.next = Node(4)\n        >>> is_cyclic(c)\n        False\n    \"\"\"\n    if not head:\n        return False\n    runner = head\n    walker = head\n    while runner.next and runner.next.next:\n        runner = runner.next.next\n        walker = walker.next\n        if runner == walker:\n            return True\n    return False\n"
  },
  {
    "path": "algorithms/linked_list/is_palindrome.py",
    "content": "\"\"\"\nPalindrome Linked List\n\nDetermine whether a singly linked list is a palindrome. Three approaches are\nprovided: reverse-half, stack-based, and dictionary-based.\n\nReference: https://leetcode.com/problems/palindrome-linked-list/\n\nComplexity (reverse-half):\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_palindrome(head: object | None) -> bool:\n    \"\"\"Check if a linked list is a palindrome by reversing the second half.\n\n    Args:\n        head: Head node of the linked list (must have .val and .next attrs).\n\n    Returns:\n        True if the list is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome(None)\n        True\n    \"\"\"\n    if not head:\n        return True\n    fast, slow = head.next, head\n    while fast and fast.next:\n        fast = fast.next.next\n        slow = slow.next\n    second = slow.next\n    slow.next = None\n    node = None\n    while second:\n        nxt = second.next\n        second.next = node\n        node = second\n        second = nxt\n    while node:\n        if node.val != head.val:\n            return False\n        node = node.next\n        head = head.next\n    return True\n\n\ndef is_palindrome_stack(head: object | None) -> bool:\n    \"\"\"Check if a linked list is a palindrome using a stack.\n\n    Args:\n        head: Head node of the linked list.\n\n    Returns:\n        True if the list is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_stack(None)\n        True\n    \"\"\"\n    if not head or not head.next:\n        return True\n\n    slow = fast = current = head\n    while fast and fast.next:\n        fast, slow = fast.next.next, slow.next\n\n    stack = [slow.val]\n    while slow.next:\n        slow = slow.next\n        stack.append(slow.val)\n\n    while stack:\n        if stack.pop() != current.val:\n            return False\n        current = current.next\n\n    return True\n\n\ndef is_palindrome_dict(head: object | None) -> bool:\n    \"\"\"Check if a linked list is a palindrome using a dictionary of positions.\n\n    Builds a dictionary mapping each value to its list of positions, then\n    verifies that positions are symmetric around the center.\n\n    Args:\n        head: Head node of the linked list.\n\n    Returns:\n        True if the list is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_dict(None)\n        True\n    \"\"\"\n    if not head or not head.next:\n        return True\n    positions: dict[object, list[int]] = {}\n    pos = 0\n    current = head\n    while current:\n        if current.val in positions:\n            positions[current.val].append(pos)\n        else:\n            positions[current.val] = [pos]\n        current = current.next\n        pos += 1\n    checksum = pos - 1\n    middle = 0\n    for indices in positions.values():\n        if len(indices) % 2 != 0:\n            middle += 1\n        else:\n            for step, i in enumerate(range(len(indices))):\n                if indices[i] + indices[len(indices) - 1 - step] != checksum:\n                    return False\n        if middle > 1:\n            return False\n    return True\n"
  },
  {
    "path": "algorithms/linked_list/is_sorted.py",
    "content": "\"\"\"\nIs Sorted Linked List\n\nGiven a linked list, determine whether the list is sorted in non-decreasing\norder. An empty list is considered sorted.\n\nReference: https://en.wikipedia.org/wiki/Linked_list\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_sorted(head: object | None) -> bool:\n    \"\"\"Check if a linked list is sorted in non-decreasing order.\n\n    Args:\n        head: Head node of the linked list (must have .val and .next attrs).\n\n    Returns:\n        True if the list is sorted or empty, False otherwise.\n\n    Examples:\n        >>> is_sorted(None)\n        True\n    \"\"\"\n    if not head:\n        return True\n    current = head\n    while current.next:\n        if current.val > current.next.val:\n            return False\n        current = current.next\n    return True\n"
  },
  {
    "path": "algorithms/linked_list/kth_to_last.py",
    "content": "\"\"\"\nKth to Last Element\n\nFind the kth to last element of a singly linked list. Three approaches are\nprovided: eval-based, dictionary-based, and two-pointer iterative.\n\nReference: https://en.wikipedia.org/wiki/Linked_list\n\nComplexity (two-pointer):\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, val: object = None) -> None:\n        self.val = val\n        self.next: Node | None = None\n\n\ndef kth_to_last_eval(head: Node, k: int) -> Node | bool:\n    \"\"\"Find the kth to last element using a safe iterative loop.\n\n    Args:\n        head: Head of the linked list.\n        k: Position from the end (1-indexed).\n\n    Returns:\n        The kth to last node, or False if k is invalid.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); a.next = b\n        >>> kth_to_last_eval(a, 1).val\n        2\n    \"\"\"\n    if not isinstance(k, int) or not head.val:\n        return False\n\n    while head:\n        seeker = head\n        for _ in range(k):\n            if seeker is None:\n                return False\n            seeker = seeker.next\n        if seeker is None:\n            return head\n        head = head.next\n\n    return False\n\n\ndef kth_to_last_dict(head: Node | None, k: int) -> Node | bool:\n    \"\"\"Find the kth to last element using a dictionary.\n\n    Args:\n        head: Head of the linked list.\n        k: Position from the end (1-indexed).\n\n    Returns:\n        The kth to last node, or False if k is invalid.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); a.next = b\n        >>> kth_to_last_dict(a, 1).val\n        2\n    \"\"\"\n    if not (head and k > -1):\n        return False\n    index_map: dict[int, Node] = {}\n    count = 0\n    while head:\n        index_map[count] = head\n        head = head.next\n        count += 1\n    return len(index_map) - k in index_map and index_map[len(index_map) - k]\n\n\ndef kth_to_last(head: Node | None, k: int) -> Node | bool:\n    \"\"\"Find the kth to last element using two pointers.\n\n    Advances the first pointer k steps ahead, then moves both pointers\n    together until the first pointer reaches the end.\n\n    Args:\n        head: Head of the linked list.\n        k: Position from the end (1-indexed).\n\n    Returns:\n        The kth to last node, or False if the list is empty.\n\n    Raises:\n        IndexError: If k exceeds the length of the list.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); a.next = b\n        >>> kth_to_last(a, 1).val\n        2\n    \"\"\"\n    if not (head or k > -1):\n        return False\n    ahead = head\n    behind = head\n    for _ in range(1, k + 1):\n        if ahead is None:\n            raise IndexError\n        ahead = ahead.next\n    while ahead:\n        ahead = ahead.next\n        behind = behind.next\n    return behind\n"
  },
  {
    "path": "algorithms/linked_list/merge_two_list.py",
    "content": "\"\"\"\nMerge Two Sorted Lists\n\nMerge two sorted linked lists into a single sorted list by splicing together\nthe nodes of the two input lists.\n\nReference: https://leetcode.com/problems/merge-two-sorted-lists/\n\nComplexity:\n    Time:  O(m + n)\n    Space: O(1) iterative, O(m + n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: int) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef merge_two_list(l1: Node | None, l2: Node | None) -> Node | None:\n    \"\"\"Merge two sorted linked lists iteratively.\n\n    Args:\n        l1: Head of the first sorted list.\n        l2: Head of the second sorted list.\n\n    Returns:\n        Head of the merged sorted list.\n\n    Examples:\n        >>> a = Node(1); a.next = Node(3)\n        >>> b = Node(2); b.next = Node(4)\n        >>> result = merge_two_list(a, b)\n        >>> result.val\n        1\n    \"\"\"\n    sentinel = current = Node(0)\n    while l1 and l2:\n        if l1.val < l2.val:\n            current.next = l1\n            l1 = l1.next\n        else:\n            current.next = l2\n            l2 = l2.next\n        current = current.next\n    current.next = l1 or l2\n    return sentinel.next\n\n\ndef merge_two_list_recur(l1: Node | None, l2: Node | None) -> Node | None:\n    \"\"\"Merge two sorted linked lists recursively.\n\n    Args:\n        l1: Head of the first sorted list.\n        l2: Head of the second sorted list.\n\n    Returns:\n        Head of the merged sorted list.\n\n    Examples:\n        >>> a = Node(1); a.next = Node(3)\n        >>> b = Node(2); b.next = Node(4)\n        >>> result = merge_two_list_recur(a, b)\n        >>> result.val\n        1\n    \"\"\"\n    if not l1 or not l2:\n        return l1 or l2\n    if l1.val < l2.val:\n        l1.next = merge_two_list_recur(l1.next, l2)\n        return l1\n    else:\n        l2.next = merge_two_list_recur(l1, l2.next)\n        return l2\n"
  },
  {
    "path": "algorithms/linked_list/partition.py",
    "content": "\"\"\"\nPartition Linked List\n\nPartition a linked list around a value x so that all nodes with values less\nthan x come before nodes with values greater than or equal to x.\n\nReference: https://leetcode.com/problems/partition-list/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, val: object = None) -> None:\n        self.val = int(val)\n        self.next: Node | None = None\n\n\ndef partition(head: Node | None, x: int) -> None:\n    \"\"\"Partition a linked list in-place around value x.\n\n    Rearranges nodes so that all nodes with values less than x appear before\n    nodes with values greater than or equal to x.\n\n    Args:\n        head: Head of the linked list.\n        x: The partition value.\n\n    Returns:\n        None. The list is modified in-place.\n\n    Examples:\n        >>> a = Node(3); b = Node(5); c = Node(1)\n        >>> a.next = b; b.next = c\n        >>> partition(a, 5)\n    \"\"\"\n    left = None\n    right = None\n    prev = None\n    current = head\n    while current:\n        if int(current.val) >= x:\n            if not right:\n                right = current\n        else:\n            if not left:\n                left = current\n            else:\n                prev.next = current.next\n                left.next = current\n                left = current\n                left.next = right\n        if prev and prev.next is None:\n            break\n        prev = current\n        current = current.next\n"
  },
  {
    "path": "algorithms/linked_list/remove_duplicates.py",
    "content": "\"\"\"\nRemove Duplicates from Linked List\n\nRemove duplicate values from an unsorted linked list. Two approaches are\nprovided: hash-set-based (O(n) time, O(n) space) and runner technique\n(O(n^2) time, O(1) space).\n\nReference: https://en.wikipedia.org/wiki/Linked_list\n\nComplexity (hash set):\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, val: object = None) -> None:\n        self.val = val\n        self.next: Node | None = None\n\n\ndef remove_dups(head: Node | None) -> None:\n    \"\"\"Remove duplicates from an unsorted linked list using a hash set.\n\n    Args:\n        head: Head of the linked list. Modified in-place.\n\n    Returns:\n        None. The list is modified in-place.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); c = Node(1)\n        >>> a.next = b; b.next = c\n        >>> remove_dups(a)\n        >>> a.next.val\n        2\n    \"\"\"\n    seen: set[object] = set()\n    prev = Node()\n    while head:\n        if head.val in seen:\n            prev.next = head.next\n        else:\n            seen.add(head.val)\n            prev = head\n        head = head.next\n\n\ndef remove_dups_wothout_set(head: Node | None) -> None:\n    \"\"\"Remove duplicates from an unsorted linked list without extra space.\n\n    Uses a runner pointer to check for duplicates of each node value.\n\n    Args:\n        head: Head of the linked list. Modified in-place.\n\n    Returns:\n        None. The list is modified in-place.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); c = Node(1)\n        >>> a.next = b; b.next = c\n        >>> remove_dups_wothout_set(a)\n        >>> a.next.val\n        2\n    \"\"\"\n    current = head\n    while current:\n        runner = current\n        while runner.next:\n            if runner.next.val == current.val:\n                runner.next = runner.next.next\n            else:\n                runner = runner.next\n        current = current.next\n"
  },
  {
    "path": "algorithms/linked_list/remove_range.py",
    "content": "\"\"\"\nRemove Range from Linked List\n\nGiven a linked list and a start and end index, remove the elements at those\nindexes (inclusive) from the list.\n\nReference: https://en.wikipedia.org/wiki/Linked_list\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef remove_range(head: object | None, start: int, end: int) -> object | None:\n    \"\"\"Remove nodes from index start to end (inclusive) from a linked list.\n\n    Args:\n        head: Head node of the linked list (must have .next attr).\n        start: Starting index of the range to remove.\n        end: Ending index of the range to remove (inclusive).\n\n    Returns:\n        The (possibly new) head of the modified list.\n\n    Examples:\n        >>> remove_range(None, 0, 0) is None\n        True\n    \"\"\"\n    assert start <= end\n    if start == 0:\n        for _ in range(end + 1):\n            if head is not None:\n                head = head.next\n    else:\n        current = head\n        for _ in range(start - 1):\n            current = current.next\n        for _ in range(end - start + 1):\n            if current is not None and current.next is not None:\n                current.next = current.next.next\n    return head\n"
  },
  {
    "path": "algorithms/linked_list/reverse.py",
    "content": "\"\"\"\nReverse Linked List\n\nReverse a singly linked list. Both iterative and recursive solutions are\nprovided.\n\nReference: https://leetcode.com/problems/reverse-linked-list/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1) iterative, O(n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef reverse_list(head: object | None) -> object | None:\n    \"\"\"Reverse a singly linked list iteratively.\n\n    Args:\n        head: Head node of the linked list (must have .next attr).\n\n    Returns:\n        The new head of the reversed list.\n\n    Examples:\n        >>> reverse_list(None) is None\n        True\n    \"\"\"\n    if not head or not head.next:\n        return head\n    prev = None\n    while head:\n        current = head\n        head = head.next\n        current.next = prev\n        prev = current\n    return prev\n\n\ndef reverse_list_recursive(head: object | None) -> object | None:\n    \"\"\"Reverse a singly linked list recursively.\n\n    Args:\n        head: Head node of the linked list (must have .next attr).\n\n    Returns:\n        The new head of the reversed list.\n\n    Examples:\n        >>> reverse_list_recursive(None) is None\n        True\n    \"\"\"\n    if head is None or head.next is None:\n        return head\n    rest = head.next\n    head.next = None\n    reversed_rest = reverse_list_recursive(rest)\n    rest.next = head\n    return reversed_rest\n"
  },
  {
    "path": "algorithms/linked_list/rotate_list.py",
    "content": "\"\"\"\nRotate List\n\nGiven a linked list, rotate the list to the right by k places, where k is\nnon-negative.\n\nReference: https://leetcode.com/problems/rotate-list/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef rotate_right(head: object | None, k: int) -> object | None:\n    \"\"\"Rotate a linked list to the right by k positions.\n\n    Args:\n        head: Head node of the linked list (must have .val and .next attrs).\n        k: Number of positions to rotate right (non-negative).\n\n    Returns:\n        The new head of the rotated list.\n\n    Examples:\n        >>> rotate_right(None, 5) is None\n        True\n    \"\"\"\n    if not head or not head.next:\n        return head\n    current = head\n    length = 1\n    while current.next:\n        current = current.next\n        length += 1\n    current.next = head\n    k = k % length\n    for _ in range(length - k):\n        current = current.next\n    head = current.next\n    current.next = None\n    return head\n"
  },
  {
    "path": "algorithms/linked_list/swap_in_pairs.py",
    "content": "\"\"\"\nSwap Nodes in Pairs\n\nGiven a linked list, swap every two adjacent nodes and return the new head.\nOnly node links are changed, not node values.\n\nReference: https://leetcode.com/problems/swap-nodes-in-pairs/\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass Node:\n    def __init__(self, x: int) -> None:\n        self.val = x\n        self.next: Node | None = None\n\n\ndef swap_pairs(head: Node | None) -> Node | None:\n    \"\"\"Swap every two adjacent nodes in a linked list.\n\n    Args:\n        head: Head of the linked list.\n\n    Returns:\n        The new head after pairwise swapping.\n\n    Examples:\n        >>> a = Node(1); b = Node(2); a.next = b\n        >>> result = swap_pairs(a)\n        >>> result.val\n        2\n    \"\"\"\n    if not head:\n        return head\n    sentinel = Node(0)\n    sentinel.next = head\n    current = sentinel\n    while current.next and current.next.next:\n        first = current.next\n        second = current.next.next\n        first.next = second.next\n        current.next = second\n        current.next.next = first\n        current = current.next.next\n    return sentinel.next\n"
  },
  {
    "path": "algorithms/map/__init__.py",
    "content": "\"\"\"Map-based algorithm implementations.\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.hash_table import HashTable, ResizableHashTable\nfrom algorithms.data_structures.separate_chaining_hash_table import (\n    SeparateChainingHashTable,\n)\n\nfrom .is_anagram import is_anagram\nfrom .is_isomorphic import is_isomorphic\nfrom .longest_common_subsequence import max_common_sub_string\nfrom .longest_palindromic_subsequence import longest_palindromic_subsequence\nfrom .randomized_set import RandomizedSet\nfrom .valid_sudoku import is_valid_sudoku\nfrom .word_pattern import word_pattern\n\n__all__ = [\n    \"HashTable\",\n    \"ResizableHashTable\",\n    \"is_anagram\",\n    \"is_isomorphic\",\n    \"is_valid_sudoku\",\n    \"longest_palindromic_subsequence\",\n    \"max_common_sub_string\",\n    \"RandomizedSet\",\n    \"SeparateChainingHashTable\",\n    \"word_pattern\",\n]\n"
  },
  {
    "path": "algorithms/map/is_anagram.py",
    "content": "\"\"\"\nIs Anagram\n\nDetermine whether two strings are anagrams of each other by comparing\ncharacter frequency maps.\n\nReference: https://leetcode.com/problems/valid-anagram/description/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_anagram(s: str, t: str) -> bool:\n    \"\"\"Check if string t is an anagram of string s.\n\n    Args:\n        s: First string.\n        t: Second string.\n\n    Returns:\n        True if t is an anagram of s, False otherwise.\n\n    Examples:\n        >>> is_anagram(\"anagram\", \"nagaram\")\n        True\n        >>> is_anagram(\"rat\", \"car\")\n        False\n    \"\"\"\n    freq_s: dict[str, int] = {}\n    freq_t: dict[str, int] = {}\n    for char in s:\n        freq_s[char] = freq_s.get(char, 0) + 1\n    for char in t:\n        freq_t[char] = freq_t.get(char, 0) + 1\n    return freq_s == freq_t\n"
  },
  {
    "path": "algorithms/map/is_isomorphic.py",
    "content": "\"\"\"\nIsomorphic Strings\n\nDetermine if two strings are isomorphic. Two strings are isomorphic if\ncharacters in s can be mapped to characters in t while preserving order,\nwith a one-to-one mapping.\n\nReference: https://leetcode.com/problems/isomorphic-strings/description/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_isomorphic(s: str, t: str) -> bool:\n    \"\"\"Check if two strings are isomorphic.\n\n    Args:\n        s: Source string.\n        t: Target string.\n\n    Returns:\n        True if s and t are isomorphic, False otherwise.\n\n    Examples:\n        >>> is_isomorphic(\"egg\", \"add\")\n        True\n        >>> is_isomorphic(\"foo\", \"bar\")\n        False\n    \"\"\"\n    if len(s) != len(t):\n        return False\n    mapping: dict[str, str] = {}\n    mapped_values: set[str] = set()\n    for i in range(len(s)):\n        if s[i] not in mapping:\n            if t[i] in mapped_values:\n                return False\n            mapping[s[i]] = t[i]\n            mapped_values.add(t[i])\n        else:\n            if mapping[s[i]] != t[i]:\n                return False\n    return True\n"
  },
  {
    "path": "algorithms/map/longest_common_subsequence.py",
    "content": "\"\"\"\nLongest Common Substring\n\nGiven two strings where the second contains all distinct characters,\nfind the longest common substring using index mapping.\n\nReference: https://en.wikipedia.org/wiki/Longest_common_substring_problem\n\nComplexity:\n    Time:  O(n log n) expected, O(n * m) worst case\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_common_sub_string(s1: str, s2: str) -> str:\n    \"\"\"Find the longest common substring between s1 and s2.\n\n    Assumes s2 has all unique characters, enabling an index-based\n    matching approach.\n\n    Args:\n        s1: First input string.\n        s2: Second input string with all distinct characters.\n\n    Returns:\n        The longest common substring.\n\n    Examples:\n        >>> max_common_sub_string(\"abcdef\", \"acdbef\")\n        'ef'\n    \"\"\"\n    char_index = {s2[i]: i for i in range(len(s2))}\n    max_length = 0\n    best_substring = \"\"\n    i = 0\n    while i < len(s1):\n        if s1[i] in char_index:\n            j = char_index[s1[i]]\n            k = i\n            while j < len(s2) and k < len(s1) and s1[k] == s2[j]:\n                k += 1\n                j += 1\n            if k - i > max_length:\n                max_length = k - i\n                best_substring = s1[i:k]\n            i = k\n        else:\n            i += 1\n    return best_substring\n"
  },
  {
    "path": "algorithms/map/longest_palindromic_subsequence.py",
    "content": "\"\"\"\nLongest Palindromic Substring\n\nFind the length of the longest palindromic substring using dynamic\nprogramming with two rolling arrays.\n\nReference: https://en.wikipedia.org/wiki/Longest_palindromic_substring\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef longest_palindromic_subsequence(s: str) -> int:\n    \"\"\"Return the length of the longest palindromic substring in s.\n\n    Args:\n        s: The input string.\n\n    Returns:\n        Length of the longest palindromic substring.\n\n    Examples:\n        >>> longest_palindromic_subsequence(\"babad\")\n        3\n        >>> longest_palindromic_subsequence(\"cbbd\")\n        2\n    \"\"\"\n    length = len(s)\n    previous_row = [0] * length\n    current_row = [0] * length\n    longest_length = 0\n\n    for end in range(length):\n        for start in range(end + 1):\n            if end - start <= 1:\n                if s[start] == s[end]:\n                    current_row[start] = 1\n                    span = end - start + 1\n                    if longest_length < span:\n                        longest_length = span\n            else:\n                if s[start] == s[end] and previous_row[start + 1]:\n                    current_row[start] = 1\n                    span = end - start + 1\n                    if longest_length < span:\n                        longest_length = span\n        previous_row = current_row\n        current_row = [0] * length\n\n    return longest_length\n"
  },
  {
    "path": "algorithms/map/randomized_set.py",
    "content": "\"\"\"\nRandomized Set\n\nDesign a data structure that supports insert, remove, and getRandom\nin average O(1) time. Uses a list for random access and a dictionary\nfor O(1) lookup/removal.\n\nReference: https://leetcode.com/problems/insert-delete-getrandom-o1/\n\nComplexity:\n    Time:  O(1) average for insert, remove, get_random\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport random\n\n\nclass RandomizedSet:\n    \"\"\"A set supporting O(1) insert, remove, and random element access.\n\n    Examples:\n        >>> rs = RandomizedSet()\n        >>> rs.insert(1)\n        True\n        >>> rs.insert(1)\n        False\n        >>> rs.remove(1)\n        True\n    \"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize the randomized set.\"\"\"\n        self.nums: list[int] = []\n        self.idxs: dict[int, int] = {}\n\n    def insert(self, val: int) -> bool:\n        \"\"\"Insert a value into the set.\n\n        Args:\n            val: Value to insert.\n\n        Returns:\n            True if the value was inserted, False if already present.\n        \"\"\"\n        if val not in self.idxs:\n            self.nums.append(val)\n            self.idxs[val] = len(self.nums) - 1\n            return True\n        return False\n\n    def remove(self, val: int) -> bool:\n        \"\"\"Remove a value from the set.\n\n        Args:\n            val: Value to remove.\n\n        Returns:\n            True if the value was removed, False if not present.\n        \"\"\"\n        if val in self.idxs:\n            idx, last = self.idxs[val], self.nums[-1]\n            self.nums[idx], self.idxs[last] = last, idx\n            self.nums.pop()\n            self.idxs.pop(val, 0)\n            return True\n        return False\n\n    def get_random(self) -> int:\n        \"\"\"Return a random element from the set.\n\n        Returns:\n            A randomly chosen element.\n        \"\"\"\n        idx = random.randint(0, len(self.nums) - 1)\n        return self.nums[idx]\n"
  },
  {
    "path": "algorithms/map/valid_sudoku.py",
    "content": "\"\"\"\nValid Sudoku\n\nDetermine if a partially filled 9x9 Sudoku board is valid. A board is\nvalid if each row, column, and 3x3 sub-box contains no duplicate digits.\n\nReference: https://leetcode.com/problems/valid-sudoku/\n\nComplexity:\n    Time:  O(1) (board is always 9x9)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_valid_sudoku(board: list[list[str]]) -> bool:\n    \"\"\"Check whether a Sudoku board configuration is valid.\n\n    Args:\n        board: 9x9 grid where empty cells are represented by '.'.\n\n    Returns:\n        True if the board is valid, False otherwise.\n\n    Examples:\n        >>> is_valid_sudoku([['.' for _ in range(9)] for _ in range(9)])\n        True\n    \"\"\"\n    seen: list[tuple[str, ...]] = []\n    for i, row in enumerate(board):\n        for j, cell in enumerate(row):\n            if cell != \".\":\n                seen += [(cell, j), (i, cell), (i // 3, j // 3, cell)]\n    return len(seen) == len(set(seen))\n"
  },
  {
    "path": "algorithms/map/word_pattern.py",
    "content": "\"\"\"\nWord Pattern\n\nGiven a pattern and a string, determine if the string follows the same\npattern via a bijection between pattern letters and words.\n\nReference: https://leetcode.com/problems/word-pattern/description/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef word_pattern(pattern: str, string: str) -> bool:\n    \"\"\"Check if a string follows the given pattern.\n\n    Args:\n        pattern: A pattern string of lowercase letters.\n        string: A space-separated string of words.\n\n    Returns:\n        True if the string follows the pattern, False otherwise.\n\n    Examples:\n        >>> word_pattern(\"abba\", \"dog cat cat dog\")\n        True\n        >>> word_pattern(\"abba\", \"dog cat cat fish\")\n        False\n    \"\"\"\n    mapping: dict[str, str] = {}\n    mapped_values: set[str] = set()\n    words = string.split()\n    if len(words) != len(pattern):\n        return False\n    for i in range(len(pattern)):\n        if pattern[i] not in mapping:\n            if words[i] in mapped_values:\n                return False\n            mapping[pattern[i]] = words[i]\n            mapped_values.add(words[i])\n        else:\n            if mapping[pattern[i]] != words[i]:\n                return False\n    return True\n"
  },
  {
    "path": "algorithms/math/__init__.py",
    "content": "\"\"\"\nCollection of mathematical algorithms and functions.\n\"\"\"\n\nfrom __future__ import annotations\n\n# Module-level imports for backward compatibility (tests use module.function syntax)\nfrom algorithms.math import (\n    chinese_remainder_theorem,  # noqa: E402\n    fft,  # noqa: E402\n    hailstone,  # noqa: E402\n    modular_inverse,  # type: ignore[no-redef]  # noqa: E402\n)\nfrom algorithms.math.base_conversion import base_to_int, int_to_base\nfrom algorithms.math.combination import combination, combination_memo\nfrom algorithms.math.cosine_similarity import cosine_similarity\nfrom algorithms.math.decimal_to_binary_ip import (\n    decimal_to_binary_ip,\n    decimal_to_binary_util,\n)\nfrom algorithms.math.diffie_hellman_key_exchange import (\n    alice_private_key,\n    alice_public_key,\n    alice_shared_key,\n    bob_private_key,\n    bob_public_key,\n    bob_shared_key,\n    diffie_hellman_key_exchange,\n)\nfrom algorithms.math.distance_between_two_points import distance_between_two_points\nfrom algorithms.math.euler_totient import euler_totient\nfrom algorithms.math.extended_gcd import extended_gcd\nfrom algorithms.math.factorial import factorial, factorial_recur\nfrom algorithms.math.find_order_simple import find_order\nfrom algorithms.math.find_primitive_root_simple import find_primitive_root\nfrom algorithms.math.gcd import gcd, gcd_bit, lcm, trailing_zero\nfrom algorithms.math.generate_strobogrammtic import (\n    gen_strobogrammatic,\n    strobogrammatic_in_range,\n)\nfrom algorithms.math.goldbach import goldbach, verify_goldbach\nfrom algorithms.math.is_strobogrammatic import is_strobogrammatic, is_strobogrammatic2\nfrom algorithms.math.krishnamurthy_number import krishnamurthy_number\nfrom algorithms.math.linear_regression import linear_regression, r_squared, rmse\nfrom algorithms.math.magic_number import magic_number\nfrom algorithms.math.manhattan_distance import manhattan_distance\nfrom algorithms.math.modular_exponential import modular_exponential\nfrom algorithms.math.next_bigger import next_bigger\nfrom algorithms.math.next_perfect_square import find_next_square, find_next_square2\nfrom algorithms.math.nth_digit import find_nth_digit\nfrom algorithms.math.num_digits import num_digits\nfrom algorithms.math.num_perfect_squares import num_perfect_squares\nfrom algorithms.math.power import power, power_recur\nfrom algorithms.math.prime_check import prime_check\nfrom algorithms.math.primes_sieve_of_eratosthenes import get_primes\nfrom algorithms.math.pythagoras import pythagoras\nfrom algorithms.math.rabin_miller import is_prime\nfrom algorithms.math.recursive_binomial_coefficient import (\n    recursive_binomial_coefficient,\n)\nfrom algorithms.math.rsa import decrypt, encrypt, generate_key\nfrom algorithms.math.sqrt_precision_factor import square_root\nfrom algorithms.math.summing_digits import sum_dig_pow\nfrom algorithms.math.surface_area_of_torus import surface_area_of_torus\n\n__all__ = [\n    \"base_to_int\",\n    \"int_to_base\",\n    \"chinese_remainder_theorem\",\n    \"combination\",\n    \"combination_memo\",\n    \"cosine_similarity\",\n    \"decimal_to_binary_ip\",\n    \"decimal_to_binary_util\",\n    \"alice_private_key\",\n    \"alice_public_key\",\n    \"alice_shared_key\",\n    \"bob_private_key\",\n    \"bob_public_key\",\n    \"bob_shared_key\",\n    \"diffie_hellman_key_exchange\",\n    \"distance_between_two_points\",\n    \"euler_totient\",\n    \"extended_gcd\",\n    \"factorial\",\n    \"factorial_recur\",\n    \"fft\",\n    \"find_order\",\n    \"find_primitive_root\",\n    \"gcd\",\n    \"gcd_bit\",\n    \"lcm\",\n    \"trailing_zero\",\n    \"gen_strobogrammatic\",\n    \"strobogrammatic_in_range\",\n    \"hailstone\",\n    \"is_strobogrammatic\",\n    \"is_strobogrammatic2\",\n    \"krishnamurthy_number\",\n    \"magic_number\",\n    \"modular_exponential\",\n    \"modular_inverse\",\n    \"next_bigger\",\n    \"find_next_square\",\n    \"find_next_square2\",\n    \"find_nth_digit\",\n    \"num_digits\",\n    \"num_perfect_squares\",\n    \"power\",\n    \"power_recur\",\n    \"prime_check\",\n    \"get_primes\",\n    \"pythagoras\",\n    \"is_prime\",\n    \"recursive_binomial_coefficient\",\n    \"decrypt\",\n    \"encrypt\",\n    \"generate_key\",\n    \"square_root\",\n    \"sum_dig_pow\",\n    \"surface_area_of_torus\",\n    \"linear_regression\",\n    \"r_squared\",\n    \"rmse\",\n    \"manhattan_distance\",\n    \"goldbach\",\n    \"verify_goldbach\",\n]\n"
  },
  {
    "path": "algorithms/math/base_conversion.py",
    "content": "\"\"\"\nInteger Base Conversion\n\nConvert integers between arbitrary bases (2-36). Supports conversion from\ninteger to string representation in a given base, and vice versa.\n\nReference: https://en.wikipedia.org/wiki/Positional_notation\n\nComplexity:\n    Time:  O(log_base(num)) for both directions\n    Space: O(log_base(num))\n\"\"\"\n\nfrom __future__ import annotations\n\nimport string\n\n\ndef int_to_base(num: int, base: int) -> str:\n    \"\"\"Convert a base-10 integer to a string in the given base.\n\n    Args:\n        num: The integer to convert.\n        base: The target base (2-36).\n\n    Returns:\n        String representation of num in the given base.\n\n    Examples:\n        >>> int_to_base(5, 2)\n        '101'\n        >>> int_to_base(255, 16)\n        'FF'\n        >>> int_to_base(0, 2)\n        '0'\n    \"\"\"\n    is_negative = False\n    if num == 0:\n        return \"0\"\n    if num < 0:\n        is_negative = True\n        num *= -1\n    digit = string.digits + string.ascii_uppercase\n    res = \"\"\n    while num > 0:\n        res += digit[num % base]\n        num //= base\n    if is_negative:\n        return \"-\" + res[::-1]\n    return res[::-1]\n\n\ndef base_to_int(str_to_convert: str, base: int) -> int:\n    \"\"\"Convert a string in a given base to a base-10 integer.\n\n    Args:\n        str_to_convert: The string representation of the number.\n        base: The base of the input string (2-36).\n\n    Returns:\n        The base-10 integer value.\n\n    Examples:\n        >>> base_to_int('101', 2)\n        5\n        >>> base_to_int('FF', 16)\n        255\n    \"\"\"\n    digit = {}\n    for ind, char in enumerate(string.digits + string.ascii_uppercase):\n        digit[char] = ind\n    multiplier = 1\n    res = 0\n    for char in str_to_convert[::-1]:\n        res += digit[char] * multiplier\n        multiplier *= base\n    return res\n"
  },
  {
    "path": "algorithms/math/chinese_remainder_theorem.py",
    "content": "\"\"\"\nChinese Remainder Theorem\n\nSolves a system of simultaneous congruences using the Chinese Remainder\nTheorem. Given pairwise coprime moduli, finds the smallest positive integer\nsatisfying all congruences.\n\nReference: https://en.wikipedia.org/wiki/Chinese_remainder_theorem\n\nComplexity:\n    Time:  O(n * m) where n is the number of equations and m is the solution\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.math.gcd import gcd\n\n\ndef solve_chinese_remainder(nums: list[int], rems: list[int]) -> int:\n    \"\"\"Find the smallest x satisfying x % nums[i] == rems[i] for all i.\n\n    Args:\n        nums: List of pairwise coprime moduli, each greater than 1.\n        rems: List of remainders corresponding to each modulus.\n\n    Returns:\n        The smallest positive integer satisfying all congruences.\n\n    Raises:\n        Exception: If inputs are invalid or moduli are not pairwise coprime.\n\n    Examples:\n        >>> solve_chinese_remainder([3, 7, 10], [2, 3, 3])\n        143\n    \"\"\"\n    if not len(nums) == len(rems):\n        raise Exception(\"nums and rems should have equal length\")\n    if not len(nums) > 0:\n        raise Exception(\"Lists nums and rems need to contain at least one element\")\n    for num in nums:\n        if not num > 1:\n            raise Exception(\"All numbers in nums needs to be > 1\")\n    if not _check_coprime(nums):\n        raise Exception(\"All pairs of numbers in nums are not coprime\")\n    k = len(nums)\n    x = 1\n    while True:\n        i = 0\n        while i < k:\n            if x % nums[i] != rems[i]:\n                break\n            i += 1\n        if i == k:\n            return x\n        x += 1\n\n\ndef _check_coprime(list_to_check: list[int]) -> bool:\n    \"\"\"Check whether all pairs of numbers in the list are coprime.\n\n    Args:\n        list_to_check: List of integers to check for pairwise coprimality.\n\n    Returns:\n        True if all pairs are coprime, False otherwise.\n    \"\"\"\n    for ind, num in enumerate(list_to_check):\n        for num2 in list_to_check[ind + 1 :]:\n            if gcd(num, num2) != 1:\n                return False\n    return True\n"
  },
  {
    "path": "algorithms/math/combination.py",
    "content": "\"\"\"\nCombinations (nCr)\n\nCalculate the number of ways to choose r items from n items (binomial\ncoefficient) using recursive and memoized approaches.\n\nReference: https://en.wikipedia.org/wiki/Combination\n\nComplexity:\n    Time:  O(2^n) naive recursive, O(n*r) memoized\n    Space: O(n) recursive stack, O(n*r) memoized\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef combination(n: int, r: int) -> int:\n    \"\"\"Calculate nCr using naive recursion.\n\n    Args:\n        n: Total number of items.\n        r: Number of items to choose.\n\n    Returns:\n        The number of combinations.\n\n    Examples:\n        >>> combination(5, 2)\n        10\n        >>> combination(10, 5)\n        252\n    \"\"\"\n    if n == r or r == 0:\n        return 1\n    return combination(n - 1, r - 1) + combination(n - 1, r)\n\n\ndef combination_memo(n: int, r: int) -> int:\n    \"\"\"Calculate nCr using memoization.\n\n    Args:\n        n: Total number of items.\n        r: Number of items to choose.\n\n    Returns:\n        The number of combinations.\n\n    Examples:\n        >>> combination_memo(50, 10)\n        10272278170\n    \"\"\"\n    memo: dict[tuple[int, int], int] = {}\n\n    def _recur(n: int, r: int) -> int:\n        if n == r or r == 0:\n            return 1\n        if (n, r) not in memo:\n            memo[(n, r)] = _recur(n - 1, r - 1) + _recur(n - 1, r)\n        return memo[(n, r)]\n\n    return _recur(n, r)\n"
  },
  {
    "path": "algorithms/math/cosine_similarity.py",
    "content": "\"\"\"\nCosine Similarity\n\nCalculate the cosine similarity between two vectors, which measures the\ncosine of the angle between them. Values range from -1 (opposite) to 1\n(identical direction).\n\nReference: https://en.wikipedia.org/wiki/Cosine_similarity\n\nComplexity:\n    Time:  O(n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef _l2_distance(vec: list[float]) -> float:\n    \"\"\"Calculate the L2 (Euclidean) norm of a vector.\n\n    Args:\n        vec: Input vector as a list of numbers.\n\n    Returns:\n        The L2 norm of the vector.\n    \"\"\"\n    norm = 0.0\n    for element in vec:\n        norm += element * element\n    norm = math.sqrt(norm)\n    return norm\n\n\ndef cosine_similarity(vec1: list[float], vec2: list[float]) -> float:\n    \"\"\"Calculate cosine similarity between two vectors.\n\n    Args:\n        vec1: First vector.\n        vec2: Second vector (must be same length as vec1).\n\n    Returns:\n        Cosine similarity value between -1 and 1.\n\n    Raises:\n        ValueError: If vectors have different lengths.\n\n    Examples:\n        >>> cosine_similarity([1, 1, 1], [1, 2, -1])\n        0.4714045207910317\n    \"\"\"\n    if len(vec1) != len(vec2):\n        raise ValueError(\n            \"The two vectors must be the same length. Got shape \"\n            + str(len(vec1))\n            + \" and \"\n            + str(len(vec2))\n        )\n\n    norm_a = _l2_distance(vec1)\n    norm_b = _l2_distance(vec2)\n\n    similarity = 0.0\n\n    for vec1_element, vec2_element in zip(vec1, vec2, strict=False):\n        similarity += vec1_element * vec2_element\n\n    similarity /= norm_a * norm_b\n\n    return similarity\n"
  },
  {
    "path": "algorithms/math/decimal_to_binary_ip.py",
    "content": "\"\"\"\nDecimal to Binary IP Conversion\n\nConvert an IP address from dotted-decimal notation to its binary\nrepresentation.\n\nReference: https://en.wikipedia.org/wiki/IP_address\n\nComplexity:\n    Time:  O(1) (fixed 4 octets, 8 bits each)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef decimal_to_binary_util(val: str) -> str:\n    \"\"\"Convert a single decimal octet (0-255) to an 8-bit binary string.\n\n    Args:\n        val: String representation of an octet value.\n\n    Returns:\n        8-character binary string.\n\n    Examples:\n        >>> decimal_to_binary_util('192')\n        '11000000'\n    \"\"\"\n    bits = [128, 64, 32, 16, 8, 4, 2, 1]\n    val_int = int(val)\n    binary_rep = \"\"\n    for bit in bits:\n        if val_int >= bit:\n            binary_rep += str(1)\n            val_int -= bit\n        else:\n            binary_rep += str(0)\n\n    return binary_rep\n\n\ndef decimal_to_binary_ip(ip: str) -> str:\n    \"\"\"Convert a dotted-decimal IP address to binary representation.\n\n    Args:\n        ip: IP address in dotted-decimal format (e.g., '192.168.0.1').\n\n    Returns:\n        Binary representation with dot-separated octets.\n\n    Examples:\n        >>> decimal_to_binary_ip('192.168.0.1')\n        '11000000.10101000.00000000.00000001'\n    \"\"\"\n    values = ip.split(\".\")\n    binary_list = []\n    for val in values:\n        binary_list.append(decimal_to_binary_util(val))\n    return \".\".join(binary_list)\n"
  },
  {
    "path": "algorithms/math/diffie_hellman_key_exchange.py",
    "content": "\"\"\"\nDiffie-Hellman Key Exchange\n\nImplements the Diffie-Hellman key exchange protocol, which enables two parties\nto establish a shared secret over an insecure channel using discrete\nlogarithm properties.\n\nReference: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange\n\nComplexity:\n    Time:  O(p) for primitive root finding, O(log(p)) for key generation\n    Space: O(p) for primitive root list\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\nimport secrets\n\n\ndef _prime_check(num: int) -> bool:\n    \"\"\"Check whether a number is prime.\n\n    Args:\n        num: The integer to test.\n\n    Returns:\n        True if num is prime, False otherwise.\n    \"\"\"\n    if num <= 1:\n        return False\n    if num == 2 or num == 3:\n        return True\n    if num % 2 == 0 or num % 3 == 0:\n        return False\n    j = 5\n    while j * j <= num:\n        if num % j == 0 or num % (j + 2) == 0:\n            return False\n        j += 6\n    return True\n\n\ndef _find_order(a: int, n: int) -> int:\n    \"\"\"Find the multiplicative order of a modulo n.\n\n    Args:\n        a: The base integer.\n        n: The modulus.\n\n    Returns:\n        The smallest positive k such that a^k = 1 (mod n), or -1 if none exists.\n    \"\"\"\n    if (a == 1) & (n == 1):\n        return 1\n    if math.gcd(a, n) != 1:\n        return -1\n    for i in range(1, n):\n        if pow(a, i) % n == 1:\n            return i\n    return -1\n\n\ndef _euler_totient(n: int) -> int:\n    \"\"\"Compute Euler's totient function phi(n).\n\n    Args:\n        n: A positive integer.\n\n    Returns:\n        The number of integers from 1 to n that are coprime with n.\n    \"\"\"\n    result = n\n    for i in range(2, int(n**0.5) + 1):\n        if n % i == 0:\n            while n % i == 0:\n                n //= i\n            result -= result // i\n    if n > 1:\n        result -= result // n\n    return result\n\n\ndef _find_primitive_root(n: int) -> list[int]:\n    \"\"\"Find all primitive roots of n.\n\n    Args:\n        n: A positive integer.\n\n    Returns:\n        List of all primitive roots of n, or empty list if none exist.\n    \"\"\"\n    if n == 1:\n        return [0]\n    phi = _euler_totient(n)\n    p_root_list = []\n    for i in range(1, n):\n        if math.gcd(i, n) != 1:\n            continue\n        order = _find_order(i, n)\n        if order == phi:\n            p_root_list.append(i)\n    return p_root_list\n\n\ndef alice_private_key(p: int) -> int:\n    \"\"\"Generate Alice's private key in range [1, p-1].\n\n    Args:\n        p: A large prime number.\n\n    Returns:\n        A random private key.\n    \"\"\"\n    return secrets.randbelow(p - 1) + 1\n\n\ndef alice_public_key(a_pr_k: int, a: int, p: int) -> int:\n    \"\"\"Calculate Alice's public key.\n\n    Args:\n        a_pr_k: Alice's private key.\n        a: The primitive root (generator).\n        p: The prime modulus.\n\n    Returns:\n        Alice's public key.\n    \"\"\"\n    return pow(a, a_pr_k, p)\n\n\ndef bob_private_key(p: int) -> int:\n    \"\"\"Generate Bob's private key in range [1, p-1].\n\n    Args:\n        p: A large prime number.\n\n    Returns:\n        A random private key.\n    \"\"\"\n    return secrets.randbelow(p - 1) + 1\n\n\ndef bob_public_key(b_pr_k: int, a: int, p: int) -> int:\n    \"\"\"Calculate Bob's public key.\n\n    Args:\n        b_pr_k: Bob's private key.\n        a: The primitive root (generator).\n        p: The prime modulus.\n\n    Returns:\n        Bob's public key.\n    \"\"\"\n    return pow(a, b_pr_k, p)\n\n\ndef alice_shared_key(b_pu_k: int, a_pr_k: int, p: int) -> int:\n    \"\"\"Calculate Alice's shared secret key.\n\n    Args:\n        b_pu_k: Bob's public key.\n        a_pr_k: Alice's private key.\n        p: The prime modulus.\n\n    Returns:\n        The shared secret key.\n    \"\"\"\n    return pow(b_pu_k, a_pr_k, p)\n\n\ndef bob_shared_key(a_pu_k: int, b_pr_k: int, p: int) -> int:\n    \"\"\"Calculate Bob's shared secret key.\n\n    Args:\n        a_pu_k: Alice's public key.\n        b_pr_k: Bob's private key.\n        p: The prime modulus.\n\n    Returns:\n        The shared secret key.\n    \"\"\"\n    return pow(a_pu_k, b_pr_k, p)\n\n\ndef diffie_hellman_key_exchange(a: int, p: int, option: int | None = None) -> bool:\n    \"\"\"Perform Diffie-Hellman key exchange and verify shared keys match.\n\n    Args:\n        a: The primitive root (generator).\n        p: A large prime number.\n        option: Unused, kept for API compatibility.\n\n    Returns:\n        True if both parties compute the same shared key, False if\n        inputs are invalid.\n\n    Examples:\n        >>> diffie_hellman_key_exchange(3, 353)\n        True\n    \"\"\"\n    if _prime_check(p) is False:\n        return False\n    try:\n        p_root_list = _find_primitive_root(p)\n        p_root_list.index(a)\n    except ValueError:\n        return False\n\n    a_pr_k = alice_private_key(p)\n    a_pu_k = alice_public_key(a_pr_k, a, p)\n\n    b_pr_k = bob_private_key(p)\n    b_pu_k = bob_public_key(b_pr_k, a, p)\n\n    a_sh_k = alice_shared_key(b_pu_k, a_pr_k, p)\n    b_sh_k = bob_shared_key(a_pu_k, b_pr_k, p)\n\n    return a_sh_k == b_sh_k\n"
  },
  {
    "path": "algorithms/math/distance_between_two_points.py",
    "content": "\"\"\"\nDistance Between Two Points in 2D Space\n\nCalculate the Euclidean distance between two points using the distance\nformula derived from the Pythagorean theorem.\n\nReference: https://en.wikipedia.org/wiki/Euclidean_distance\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom math import sqrt\n\n\ndef distance_between_two_points(x1: float, y1: float, x2: float, y2: float) -> float:\n    \"\"\"Calculate the Euclidean distance between two points in 2D space.\n\n    Args:\n        x1: x-coordinate of the first point.\n        y1: y-coordinate of the first point.\n        x2: x-coordinate of the second point.\n        y2: y-coordinate of the second point.\n\n    Returns:\n        The Euclidean distance between the two points.\n\n    Examples:\n        >>> distance_between_two_points(0, 0, 3, 4)\n        5.0\n        >>> distance_between_two_points(-1, -1, 2, 3)\n        5.0\n    \"\"\"\n    return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n"
  },
  {
    "path": "algorithms/math/euler_totient.py",
    "content": "\"\"\"\nEuler's Totient Function\n\nCompute Euler's totient function phi(n), which counts the number of integers\nfrom 1 to n inclusive that are coprime to n.\n\nReference: https://en.wikipedia.org/wiki/Euler%27s_totient_function\n\nComplexity:\n    Time:  O(sqrt(n))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef euler_totient(n: int) -> int:\n    \"\"\"Compute Euler's totient function phi(n).\n\n    Args:\n        n: A positive integer.\n\n    Returns:\n        The count of integers in [1, n] coprime to n.\n\n    Examples:\n        >>> euler_totient(8)\n        4\n        >>> euler_totient(21)\n        12\n    \"\"\"\n    result = n\n    for i in range(2, int(n**0.5) + 1):\n        if n % i == 0:\n            while n % i == 0:\n                n //= i\n            result -= result // i\n    if n > 1:\n        result -= result // n\n    return result\n"
  },
  {
    "path": "algorithms/math/extended_gcd.py",
    "content": "\"\"\"\nExtended Euclidean Algorithm\n\nFind coefficients s and t (Bezout's identity) such that:\nnum1 * s + num2 * t = gcd(num1, num2).\n\nReference: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm\n\nComplexity:\n    Time:  O(log(min(num1, num2)))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef extended_gcd(num1: int, num2: int) -> tuple[int, int, int]:\n    \"\"\"Compute the extended GCD of two integers.\n\n    Args:\n        num1: First integer.\n        num2: Second integer.\n\n    Returns:\n        A tuple (s, t, g) where num1 * s + num2 * t = g = gcd(num1, num2).\n\n    Examples:\n        >>> extended_gcd(8, 2)\n        (0, 1, 2)\n        >>> extended_gcd(13, 17)\n        (0, 1, 17)\n    \"\"\"\n    old_s, s = 1, 0\n    old_t, t = 0, 1\n    old_r, r = num1, num2\n\n    while r != 0:\n        quotient = old_r / r\n\n        old_r, r = r, old_r - quotient * r\n        old_s, s = s, old_s - quotient * s\n        old_t, t = t, old_t - quotient * t\n\n    return old_s, old_t, old_r\n"
  },
  {
    "path": "algorithms/math/factorial.py",
    "content": "\"\"\"\nFactorial\n\nCompute the factorial of a non-negative integer, with optional modular\narithmetic support.\n\nReference: https://en.wikipedia.org/wiki/Factorial\n\nComplexity:\n    Time:  O(n)\n    Space: O(1) iterative, O(n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef factorial(n: int, mod: int | None = None) -> int:\n    \"\"\"Calculate n! iteratively, optionally modulo mod.\n\n    Args:\n        n: A non-negative integer.\n        mod: Optional positive integer modulus.\n\n    Returns:\n        n! or n! % mod if mod is provided.\n\n    Raises:\n        ValueError: If n is negative or mod is not a positive integer.\n\n    Examples:\n        >>> factorial(5)\n        120\n        >>> factorial(10)\n        3628800\n    \"\"\"\n    if not (isinstance(n, int) and n >= 0):\n        raise ValueError(\"'n' must be a non-negative integer.\")\n    if mod is not None and not (isinstance(mod, int) and mod > 0):\n        raise ValueError(\"'mod' must be a positive integer\")\n    result = 1\n    if n == 0:\n        return 1\n    for i in range(2, n + 1):\n        result *= i\n        if mod:\n            result %= mod\n    return result\n\n\ndef factorial_recur(n: int, mod: int | None = None) -> int:\n    \"\"\"Calculate n! recursively, optionally modulo mod.\n\n    Args:\n        n: A non-negative integer.\n        mod: Optional positive integer modulus.\n\n    Returns:\n        n! or n! % mod if mod is provided.\n\n    Raises:\n        ValueError: If n is negative or mod is not a positive integer.\n\n    Examples:\n        >>> factorial_recur(5)\n        120\n    \"\"\"\n    if not (isinstance(n, int) and n >= 0):\n        raise ValueError(\"'n' must be a non-negative integer.\")\n    if mod is not None and not (isinstance(mod, int) and mod > 0):\n        raise ValueError(\"'mod' must be a positive integer\")\n    if n == 0:\n        return 1\n    result = n * factorial(n - 1, mod)\n    if mod:\n        result %= mod\n    return result\n"
  },
  {
    "path": "algorithms/math/fft.py",
    "content": "\"\"\"\nFast Fourier Transform (Cooley-Tukey)\n\nCompute the Discrete Fourier Transform of a sequence using the Cooley-Tukey\nradix-2 decimation-in-time algorithm. Input length must be a power of 2.\n\nReference: https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm\n\nComplexity:\n    Time:  O(n log n)\n    Space: O(n log n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom cmath import exp, pi\n\n\ndef fft(x: list[complex]) -> list[complex]:\n    \"\"\"Compute the FFT of a sequence using the Cooley-Tukey algorithm.\n\n    Args:\n        x: Input array of complex values. Length must be a power of 2.\n\n    Returns:\n        The Discrete Fourier Transform of x.\n\n    Examples:\n        >>> fft([1.0, 1.0, 1.0, 1.0])\n        [(4+0j), 0j, 0j, 0j]\n    \"\"\"\n    n = len(x)\n    if n == 1:\n        return x\n\n    even = fft(x[0::2])\n    odd = fft(x[1::2])\n\n    y = [0 for _ in range(n)]\n    for k in range(n // 2):\n        q = exp(-2j * pi * k / n) * odd[k]\n        y[k] = even[k] + q\n        y[k + n // 2] = even[k] - q\n\n    return y\n"
  },
  {
    "path": "algorithms/math/find_order_simple.py",
    "content": "\"\"\"\nMultiplicative Order\n\nFind the multiplicative order of a modulo n, which is the smallest positive\ninteger k such that a^k = 1 (mod n). Requires gcd(a, n) = 1.\n\nReference: https://en.wikipedia.org/wiki/Multiplicative_order\n\nComplexity:\n    Time:  O(n log n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef find_order(a: int, n: int) -> int:\n    \"\"\"Find the multiplicative order of a modulo n.\n\n    Args:\n        a: The base integer.\n        n: The modulus.\n\n    Returns:\n        The smallest positive k where a^k = 1 (mod n), or -1 if a and n\n        are not coprime.\n\n    Examples:\n        >>> find_order(3, 7)\n        6\n        >>> find_order(1, 1)\n        1\n    \"\"\"\n    if (a == 1) & (n == 1):\n        return 1\n    if math.gcd(a, n) != 1:\n        return -1\n    for i in range(1, n):\n        if pow(a, i) % n == 1:\n            return i\n    return -1\n"
  },
  {
    "path": "algorithms/math/find_primitive_root_simple.py",
    "content": "\"\"\"\nPrimitive Root Finder\n\nFind all primitive roots of a positive integer n. A primitive root modulo n\nis an integer whose multiplicative order modulo n equals Euler's totient\nof n.\n\nReference: https://en.wikipedia.org/wiki/Primitive_root_modulo_n\n\nComplexity:\n    Time:  O(n^2 log n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef _find_order(a: int, n: int) -> int:\n    \"\"\"Find the multiplicative order of a modulo n.\n\n    Args:\n        a: The base integer.\n        n: The modulus.\n\n    Returns:\n        The smallest positive k where a^k = 1 (mod n), or -1 if none exists.\n    \"\"\"\n    if (a == 1) & (n == 1):\n        return 1\n    if math.gcd(a, n) != 1:\n        return -1\n    for i in range(1, n):\n        if pow(a, i) % n == 1:\n            return i\n    return -1\n\n\ndef _euler_totient(n: int) -> int:\n    \"\"\"Compute Euler's totient function phi(n).\n\n    Args:\n        n: A positive integer.\n\n    Returns:\n        The count of integers in [1, n] coprime to n.\n    \"\"\"\n    result = n\n    for i in range(2, int(n**0.5) + 1):\n        if n % i == 0:\n            while n % i == 0:\n                n //= i\n            result -= result // i\n    if n > 1:\n        result -= result // n\n    return result\n\n\ndef find_primitive_root(n: int) -> list[int]:\n    \"\"\"Find all primitive roots of n.\n\n    Args:\n        n: A positive integer.\n\n    Returns:\n        List of all primitive roots of n. Returns [0] for n=1, or an\n        empty list if no primitive roots exist.\n\n    Examples:\n        >>> find_primitive_root(5)\n        [2, 3]\n        >>> find_primitive_root(1)\n        [0]\n    \"\"\"\n    if n == 1:\n        return [0]\n    phi = _euler_totient(n)\n    p_root_list = []\n    for i in range(1, n):\n        if math.gcd(i, n) == 1:\n            order = _find_order(i, n)\n            if order == phi:\n                p_root_list.append(i)\n    return p_root_list\n"
  },
  {
    "path": "algorithms/math/gcd.py",
    "content": "\"\"\"\nGreatest Common Divisor and Least Common Multiple\n\nCompute the GCD and LCM of two integers using Euclid's algorithm and\na bitwise variant.\n\nReference: https://en.wikipedia.org/wiki/Euclidean_algorithm\n\nComplexity:\n    Time:  O(log(min(a, b))) for gcd, O(log(min(a, b))) for lcm\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef gcd(a: int, b: int) -> int:\n    \"\"\"Compute the greatest common divisor using Euclid's algorithm.\n\n    Args:\n        a: First integer (non-zero).\n        b: Second integer (non-zero).\n\n    Returns:\n        The greatest common divisor of a and b.\n\n    Raises:\n        ValueError: If inputs are not integers or either is zero.\n\n    Examples:\n        >>> gcd(8, 12)\n        4\n        >>> gcd(13, 17)\n        1\n    \"\"\"\n    a_int = isinstance(a, int)\n    b_int = isinstance(b, int)\n    a = abs(a)\n    b = abs(b)\n    if not (a_int or b_int):\n        raise ValueError(\"Input arguments are not integers\")\n\n    if (a == 0) or (b == 0):\n        raise ValueError(\"One or more input arguments equals zero\")\n\n    while b != 0:\n        a, b = b, a % b\n    return a\n\n\ndef lcm(a: int, b: int) -> int:\n    \"\"\"Compute the lowest common multiple of two integers.\n\n    Args:\n        a: First integer (non-zero).\n        b: Second integer (non-zero).\n\n    Returns:\n        The lowest common multiple of a and b.\n\n    Examples:\n        >>> lcm(8, 12)\n        24\n    \"\"\"\n    return abs(a) * abs(b) / gcd(a, b)\n\n\ndef trailing_zero(x: int) -> int:\n    \"\"\"Count the number of trailing zeros in the binary representation.\n\n    Args:\n        x: A positive integer.\n\n    Returns:\n        Number of trailing zero bits.\n\n    Examples:\n        >>> trailing_zero(34)\n        1\n        >>> trailing_zero(40)\n        3\n    \"\"\"\n    count = 0\n    while x and not x & 1:\n        count += 1\n        x >>= 1\n    return count\n\n\ndef gcd_bit(a: int, b: int) -> int:\n    \"\"\"Compute GCD using the binary (Stein's) algorithm.\n\n    Args:\n        a: First non-negative integer.\n        b: Second non-negative integer.\n\n    Returns:\n        The greatest common divisor of a and b.\n\n    Examples:\n        >>> gcd_bit(8, 12)\n        4\n    \"\"\"\n    tza = trailing_zero(a)\n    tzb = trailing_zero(b)\n    a >>= tza\n    b >>= tzb\n    while b:\n        if a < b:\n            a, b = b, a\n        a -= b\n        a >>= trailing_zero(a)\n    return a << min(tza, tzb)\n"
  },
  {
    "path": "algorithms/math/generate_strobogrammtic.py",
    "content": "\"\"\"\nGenerate Strobogrammatic Numbers\n\nA strobogrammatic number looks the same when rotated 180 degrees. Generate\nall strobogrammatic numbers of a given length or count them within a range.\n\nReference: https://en.wikipedia.org/wiki/Strobogrammatic_number\n\nComplexity:\n    Time:  O(5^(n/2)) for generation\n    Space: O(5^(n/2))\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef gen_strobogrammatic(n: int) -> list[str]:\n    \"\"\"Generate all strobogrammatic numbers of length n.\n\n    Args:\n        n: The desired length of strobogrammatic numbers.\n\n    Returns:\n        List of strobogrammatic number strings.\n\n    Examples:\n        >>> gen_strobogrammatic(2)\n        ['88', '11', '96', '69']\n    \"\"\"\n    return _helper(n, n)\n\n\ndef _helper(n: int, length: int) -> list[str]:\n    \"\"\"Recursively build strobogrammatic numbers of length n.\n\n    Args:\n        n: Remaining length to fill.\n        length: Original target length.\n\n    Returns:\n        List of strobogrammatic number strings.\n    \"\"\"\n    if n == 0:\n        return [\"\"]\n    if n == 1:\n        return [\"1\", \"0\", \"8\"]\n    middles = _helper(n - 2, length)\n    result = []\n    for middle in middles:\n        if n != length:\n            result.append(\"0\" + middle + \"0\")\n        result.append(\"8\" + middle + \"8\")\n        result.append(\"1\" + middle + \"1\")\n        result.append(\"9\" + middle + \"6\")\n        result.append(\"6\" + middle + \"9\")\n    return result\n\n\ndef strobogrammatic_in_range(low: str, high: str) -> int:\n    \"\"\"Count strobogrammatic numbers within the given range [low, high].\n\n    Args:\n        low: Lower bound as a string.\n        high: Upper bound as a string.\n\n    Returns:\n        Count of strobogrammatic numbers in the range.\n\n    Examples:\n        >>> strobogrammatic_in_range(\"10\", \"100\")\n        4\n    \"\"\"\n    res: list[str] = []\n    count = 0\n    low_len = len(low)\n    high_len = len(high)\n    for i in range(low_len, high_len + 1):\n        res.extend(_helper2(i, i))\n    for perm in res:\n        if len(perm) == low_len and int(perm) < int(low):\n            continue\n        if len(perm) == high_len and int(perm) > int(high):\n            continue\n        count += 1\n    return count\n\n\ndef _helper2(n: int, length: int) -> list[str]:\n    \"\"\"Recursively build strobogrammatic numbers including leading zeros.\n\n    Args:\n        n: Remaining length to fill.\n        length: Original target length.\n\n    Returns:\n        List of strobogrammatic number strings.\n    \"\"\"\n    if n == 0:\n        return [\"\"]\n    if n == 1:\n        return [\"0\", \"8\", \"1\"]\n    mids = _helper(n - 2, length)\n    res = []\n    for mid in mids:\n        if n != length:\n            res.append(\"0\" + mid + \"0\")\n        res.append(\"1\" + mid + \"1\")\n        res.append(\"6\" + mid + \"9\")\n        res.append(\"9\" + mid + \"6\")\n        res.append(\"8\" + mid + \"8\")\n    return res\n"
  },
  {
    "path": "algorithms/math/goldbach.py",
    "content": "\"\"\"\nGoldbach's Conjecture\n\nEvery even integer greater than 2 can be expressed as the sum of two primes.\nThis module provides a function to find such a pair of primes and a helper\nto verify the conjecture over a range.\n\nReference: https://en.wikipedia.org/wiki/Goldbach%27s_conjecture\n\nComplexity:\n    Time:  O(n * sqrt(n))  per call (sieve could be used for batch queries)\n    Space: O(1) per call\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _is_prime(n: int) -> bool:\n    \"\"\"Return ``True`` if *n* is a prime number.\n\n    Examples:\n        >>> _is_prime(7)\n        True\n        >>> _is_prime(10)\n        False\n    \"\"\"\n    if n < 2:\n        return False\n    if n < 4:\n        return True\n    if n % 2 == 0 or n % 3 == 0:\n        return False\n    i = 5\n    while i * i <= n:\n        if n % i == 0 or n % (i + 2) == 0:\n            return False\n        i += 6\n    return True\n\n\ndef goldbach(n: int) -> tuple[int, int]:\n    \"\"\"Return two primes whose sum equals *n*.\n\n    Args:\n        n: An even integer greater than 2.\n\n    Returns:\n        A tuple ``(p, q)`` with ``p <= q`` and ``p + q == n`` where both\n        ``p`` and ``q`` are prime.\n\n    Raises:\n        ValueError: If *n* is not an even integer greater than 2.\n\n    Examples:\n        >>> goldbach(4)\n        (2, 2)\n        >>> goldbach(28)\n        (5, 23)\n        >>> p, q = goldbach(100)\n        >>> p + q == 100 and _is_prime(p) and _is_prime(q)\n        True\n    \"\"\"\n    if n <= 2 or n % 2 != 0:\n        msg = f\"n must be an even integer greater than 2, got {n}\"\n        raise ValueError(msg)\n\n    for i in range(2, n // 2 + 1):\n        if _is_prime(i) and _is_prime(n - i):\n            return (i, n - i)\n\n    # Should never be reached if Goldbach's conjecture holds\n    msg = f\"no prime pair found for {n}\"  # pragma: no cover\n    raise RuntimeError(msg)  # pragma: no cover\n\n\ndef verify_goldbach(limit: int) -> bool:\n    \"\"\"Verify Goldbach's conjecture for all even numbers from 4 to *limit*.\n\n    Args:\n        limit: Upper bound (inclusive) for verification.\n\n    Returns:\n        ``True`` if every even number in range can be expressed as a sum\n        of two primes.\n\n    Examples:\n        >>> verify_goldbach(100)\n        True\n        >>> verify_goldbach(1000)\n        True\n    \"\"\"\n    return all(goldbach(n) is not None for n in range(4, limit + 1, 2))\n"
  },
  {
    "path": "algorithms/math/hailstone.py",
    "content": "\"\"\"\nHailstone Sequence (Collatz Conjecture)\n\nGenerate the hailstone sequence starting from n: if n is even, next is n/2;\nif n is odd, next is 3n + 1. The sequence ends when it reaches 1.\n\nReference: https://en.wikipedia.org/wiki/Collatz_conjecture\n\nComplexity:\n    Time:  O(unknown) - conjectured to always terminate\n    Space: O(sequence length)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef hailstone(n: int) -> list[int]:\n    \"\"\"Generate the hailstone sequence from n to 1.\n\n    Args:\n        n: The starting positive integer.\n\n    Returns:\n        The complete hailstone sequence from n down to 1.\n\n    Examples:\n        >>> hailstone(8)\n        [8, 4, 2, 1]\n        >>> hailstone(10)\n        [10, 5, 16, 8, 4, 2, 1]\n    \"\"\"\n    sequence = [n]\n    while n > 1:\n        n = 3 * n + 1 if n % 2 != 0 else int(n / 2)\n        sequence.append(n)\n    return sequence\n"
  },
  {
    "path": "algorithms/math/is_strobogrammatic.py",
    "content": "\"\"\"\nStrobogrammatic Number Check\n\nDetermine whether a number (as a string) is strobogrammatic, meaning it\nlooks the same when rotated 180 degrees.\n\nReference: https://en.wikipedia.org/wiki/Strobogrammatic_number\n\nComplexity:\n    Time:  O(n) where n is the length of the number string\n    Space: O(1) for is_strobogrammatic, O(n) for is_strobogrammatic2\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_strobogrammatic(num: str) -> bool:\n    \"\"\"Check if a number string is strobogrammatic using two pointers.\n\n    Args:\n        num: String representation of the number.\n\n    Returns:\n        True if num is strobogrammatic, False otherwise.\n\n    Examples:\n        >>> is_strobogrammatic(\"69\")\n        True\n        >>> is_strobogrammatic(\"14\")\n        False\n    \"\"\"\n    comb = \"00 11 88 69 96\"\n    i = 0\n    j = len(num) - 1\n    while i <= j:\n        if comb.find(num[i] + num[j]) == -1:\n            return False\n        i += 1\n        j -= 1\n    return True\n\n\ndef is_strobogrammatic2(num: str) -> bool:\n    \"\"\"Check if a number string is strobogrammatic using string reversal.\n\n    Args:\n        num: String representation of the number.\n\n    Returns:\n        True if num is strobogrammatic, False otherwise.\n\n    Examples:\n        >>> is_strobogrammatic2(\"69\")\n        True\n        >>> is_strobogrammatic2(\"14\")\n        False\n    \"\"\"\n    return num == num[::-1].replace(\"6\", \"#\").replace(\"9\", \"6\").replace(\"#\", \"9\")\n"
  },
  {
    "path": "algorithms/math/krishnamurthy_number.py",
    "content": "\"\"\"\nKrishnamurthy Number\n\nA Krishnamurthy number is a number whose sum of the factorials of its digits\nequals the number itself (e.g., 145 = 1! + 4! + 5!).\n\nReference: https://en.wikipedia.org/wiki/Factorion\n\nComplexity:\n    Time:  O(d * m) where d is number of digits and m is max digit value\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _find_factorial(n: int) -> int:\n    \"\"\"Calculate the factorial of a non-negative integer.\n\n    Args:\n        n: A non-negative integer.\n\n    Returns:\n        The factorial of n.\n    \"\"\"\n    fact = 1\n    while n != 0:\n        fact *= n\n        n -= 1\n    return fact\n\n\ndef krishnamurthy_number(n: int) -> bool:\n    \"\"\"Check if n is a Krishnamurthy number (factorion).\n\n    Args:\n        n: The integer to check.\n\n    Returns:\n        True if n is a Krishnamurthy number, False otherwise.\n\n    Examples:\n        >>> krishnamurthy_number(145)\n        True\n        >>> krishnamurthy_number(357)\n        False\n    \"\"\"\n    if n == 0:\n        return False\n    sum_of_digits = 0\n    temp = n\n\n    while temp != 0:\n        sum_of_digits += _find_factorial(temp % 10)\n        temp //= 10\n\n    return sum_of_digits == n\n"
  },
  {
    "path": "algorithms/math/linear_regression.py",
    "content": "\"\"\"Simple linear regression — fit a line to (x, y) data.\n\nComputes the ordinary least-squares regression line y = mx + b\nwithout external libraries.\n\nInspired by PR #871 (MakanFar).\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef linear_regression(x: list[float], y: list[float]) -> tuple[float, float]:\n    \"\"\"Return (slope, intercept) for the best-fit line.\n\n    >>> m, b = linear_regression([1, 2, 3, 4, 5], [2, 4, 5, 4, 5])\n    >>> round(m, 4)\n    0.6\n    >>> round(b, 4)\n    2.2\n    \"\"\"\n    n = len(x)\n    if n != len(y) or n < 2:\n        msg = \"x and y must have at least 2 equal-length elements\"\n        raise ValueError(msg)\n    sum_x = sum(x)\n    sum_y = sum(y)\n    sum_xy = sum(xi * yi for xi, yi in zip(x, y, strict=False))\n    sum_x2 = sum(xi * xi for xi in x)\n    denom = n * sum_x2 - sum_x * sum_x\n    if denom == 0:\n        msg = \"Vertical line — slope is undefined\"\n        raise ValueError(msg)\n    slope = (n * sum_xy - sum_x * sum_y) / denom\n    intercept = (sum_y - slope * sum_x) / n\n    return slope, intercept\n\n\ndef r_squared(x: list[float], y: list[float]) -> float:\n    \"\"\"Return the R-squared (coefficient of determination) for the fit.\"\"\"\n    slope, intercept = linear_regression(x, y)\n    y_mean = sum(y) / len(y)\n    ss_tot = sum((yi - y_mean) ** 2 for yi in y)\n    ss_res = sum(\n        (yi - (slope * xi + intercept)) ** 2 for xi, yi in zip(x, y, strict=False)\n    )\n    if ss_tot == 0:\n        return 1.0\n    return 1.0 - ss_res / ss_tot\n\n\ndef rmse(x: list[float], y: list[float]) -> float:\n    \"\"\"Return the root mean squared error for the fit.\"\"\"\n    slope, intercept = linear_regression(x, y)\n    n = len(y)\n    return math.sqrt(\n        sum((yi - (slope * xi + intercept)) ** 2 for xi, yi in zip(x, y, strict=False))\n        / n\n    )\n"
  },
  {
    "path": "algorithms/math/magic_number.py",
    "content": "\"\"\"\nMagic Number\n\nA magic number is a number where recursively summing its digits eventually\nyields 1. For example, 199 -> 1+9+9=19 -> 1+9=10 -> 1+0=1.\n\nReference: https://en.wikipedia.org/wiki/Digital_root\n\nComplexity:\n    Time:  O(log n) amortized\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef magic_number(n: int) -> bool:\n    \"\"\"Check if n is a magic number (digital root equals 1).\n\n    Args:\n        n: The integer to check.\n\n    Returns:\n        True if the digital root of n is 1, False otherwise.\n\n    Examples:\n        >>> magic_number(1234)\n        True\n        >>> magic_number(111)\n        False\n    \"\"\"\n    total_sum = 0\n\n    while n > 0 or total_sum > 9:\n        if n == 0:\n            n = total_sum\n            total_sum = 0\n        total_sum += n % 10\n        n //= 10\n\n    return total_sum == 1\n"
  },
  {
    "path": "algorithms/math/manhattan_distance.py",
    "content": "\"\"\"Manhattan distance — L1 distance between two points.\n\nAlso known as taxicab distance or city-block distance, it is the sum\nof absolute differences of coordinates.\n\nInspired by PR #877 (ChinZhengSheng).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef manhattan_distance(a: tuple[float, ...], b: tuple[float, ...]) -> float:\n    \"\"\"Return the Manhattan (L1) distance between points *a* and *b*.\n\n    Works in any number of dimensions.\n\n    >>> manhattan_distance((1, 2), (4, 6))\n    7\n    >>> manhattan_distance((0, 0, 0), (1, 2, 3))\n    6\n    \"\"\"\n    return sum(abs(x - y) for x, y in zip(a, b, strict=False))\n"
  },
  {
    "path": "algorithms/math/modular_exponential.py",
    "content": "\"\"\"\nModular Exponentiation\n\nCompute (base ^ exponent) % mod efficiently using binary exponentiation\n(repeated squaring).\n\nReference: https://en.wikipedia.org/wiki/Modular_exponentiation\n\nComplexity:\n    Time:  O(log exponent)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef modular_exponential(base: int, exponent: int, mod: int) -> int:\n    \"\"\"Compute (base ^ exponent) % mod using binary exponentiation.\n\n    Args:\n        base: The base value.\n        exponent: The exponent (must be non-negative).\n        mod: The modulus.\n\n    Returns:\n        The result of (base ^ exponent) % mod.\n\n    Raises:\n        ValueError: If exponent is negative.\n\n    Examples:\n        >>> modular_exponential(5, 117, 19)\n        1\n    \"\"\"\n    if exponent < 0:\n        raise ValueError(\"Exponent must be positive.\")\n    base %= mod\n    result = 1\n\n    while exponent > 0:\n        if exponent & 1:\n            result = (result * base) % mod\n        exponent = exponent >> 1\n        base = (base * base) % mod\n\n    return result\n"
  },
  {
    "path": "algorithms/math/modular_inverse.py",
    "content": "\"\"\"\nModular Multiplicative Inverse\n\nFind x such that a * x = 1 (mod m) using the Extended Euclidean Algorithm.\nRequires a and m to be coprime.\n\nReference: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse\n\nComplexity:\n    Time:  O(log(min(a, m)))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _extended_gcd(a: int, b: int) -> tuple[int, int, int]:\n    \"\"\"Compute the extended GCD of two integers.\n\n    Args:\n        a: First integer.\n        b: Second integer.\n\n    Returns:\n        A tuple (s, t, g) where a * s + b * t = g = gcd(a, b).\n    \"\"\"\n    old_s, s = 1, 0\n    old_t, t = 0, 1\n    old_r, r = a, b\n\n    while r != 0:\n        quotient = old_r // r\n\n        old_r, r = r, old_r - quotient * r\n        old_s, s = s, old_s - quotient * s\n        old_t, t = t, old_t - quotient * t\n\n    return old_s, old_t, old_r\n\n\ndef modular_inverse(a: int, m: int) -> int:\n    \"\"\"Find x such that a * x = 1 (mod m).\n\n    Args:\n        a: The integer to find the inverse of.\n        m: The modulus (must be coprime with a).\n\n    Returns:\n        The modular multiplicative inverse of a modulo m.\n\n    Raises:\n        ValueError: If a and m are not coprime.\n\n    Examples:\n        >>> modular_inverse(2, 19)\n        10\n    \"\"\"\n    s, _, g = _extended_gcd(a, m)\n    if g != 1:\n        raise ValueError(\"a and m must be coprime\")\n    return s % m\n"
  },
  {
    "path": "algorithms/math/next_bigger.py",
    "content": "\"\"\"\nNext Bigger Number with Same Digits\n\nGiven a number, find the next higher number that uses the exact same set of\ndigits. This is equivalent to finding the next permutation.\n\nReference: https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order\n\nComplexity:\n    Time:  O(n) where n is the number of digits\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef next_bigger(num: int) -> int:\n    \"\"\"Find the next higher number with the exact same digits.\n\n    Args:\n        num: A positive integer.\n\n    Returns:\n        The next higher number with the same digits, or -1 if no such\n        number exists.\n\n    Examples:\n        >>> next_bigger(38276)\n        38627\n        >>> next_bigger(99999)\n        -1\n    \"\"\"\n    digits = [int(i) for i in str(num)]\n    idx = len(digits) - 1\n\n    while idx >= 1 and digits[idx - 1] >= digits[idx]:\n        idx -= 1\n\n    if idx == 0:\n        return -1\n\n    pivot = digits[idx - 1]\n    swap_idx = len(digits) - 1\n\n    while pivot >= digits[swap_idx]:\n        swap_idx -= 1\n\n    digits[swap_idx], digits[idx - 1] = digits[idx - 1], digits[swap_idx]\n    digits[idx:] = digits[: idx - 1 : -1]\n\n    return int(\"\".join(str(x) for x in digits))\n"
  },
  {
    "path": "algorithms/math/next_perfect_square.py",
    "content": "\"\"\"\nNext Perfect Square\n\nGiven a number, find the next perfect square if the input is itself a perfect\nsquare. Otherwise, return -1.\n\nReference: https://en.wikipedia.org/wiki/Square_number\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_next_square(sq: float) -> float:\n    \"\"\"Find the next perfect square after sq.\n\n    Args:\n        sq: A non-negative number to check.\n\n    Returns:\n        The next perfect square if sq is a perfect square, otherwise -1.\n\n    Examples:\n        >>> find_next_square(121)\n        144\n        >>> find_next_square(10)\n        -1\n    \"\"\"\n    root = sq**0.5\n    if root.is_integer():\n        return (root + 1) ** 2\n    return -1\n\n\ndef find_next_square2(sq: float) -> float:\n    \"\"\"Find the next perfect square using modulo check.\n\n    Args:\n        sq: A non-negative number to check.\n\n    Returns:\n        The next perfect square if sq is a perfect square, otherwise -1.\n\n    Examples:\n        >>> find_next_square2(121)\n        144\n        >>> find_next_square2(10)\n        -1\n    \"\"\"\n    root = sq**0.5\n    return -1 if root % 1 else (root + 1) ** 2\n"
  },
  {
    "path": "algorithms/math/nth_digit.py",
    "content": "\"\"\"\nFind the Nth Digit\n\nFind the nth digit in the infinite sequence 1, 2, 3, ..., 9, 10, 11, 12, ...\nby determining which number contains it and extracting the specific digit.\n\nReference: https://en.wikipedia.org/wiki/Positional_notation\n\nComplexity:\n    Time:  O(log n)\n    Space: O(log n) for string conversion\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_nth_digit(n: int) -> int:\n    \"\"\"Find the nth digit in the sequence of natural numbers.\n\n    Args:\n        n: The 1-based position of the digit to find.\n\n    Returns:\n        The digit at position n.\n\n    Examples:\n        >>> find_nth_digit(11)\n        0\n    \"\"\"\n    length = 1\n    count = 9\n    start = 1\n    while n > length * count:\n        n -= length * count\n        length += 1\n        count *= 10\n        start *= 10\n    start += (n - 1) / length\n    s = str(start)\n    return int(s[(n - 1) % length])\n"
  },
  {
    "path": "algorithms/math/num_digits.py",
    "content": "\"\"\"\nNumber of Digits\n\nCount the number of digits in an integer using logarithmic computation\nfor O(1) time complexity.\n\nReference: https://en.wikipedia.org/wiki/Logarithm\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef num_digits(n: int) -> int:\n    \"\"\"Count the number of digits in an integer.\n\n    Args:\n        n: An integer (negative values use their absolute value).\n\n    Returns:\n        The number of digits in n.\n\n    Examples:\n        >>> num_digits(12)\n        2\n        >>> num_digits(0)\n        1\n        >>> num_digits(-254)\n        3\n    \"\"\"\n    n = abs(n)\n    if n == 0:\n        return 1\n    return int(math.log10(n)) + 1\n"
  },
  {
    "path": "algorithms/math/num_perfect_squares.py",
    "content": "\"\"\"\nMinimum Perfect Squares Sum\n\nDetermine the minimum number of perfect squares that sum to a given integer.\nBy Lagrange's four-square theorem, the answer is always between 1 and 4.\n\nReference: https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theorem\n\nComplexity:\n    Time:  O(sqrt(n))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef num_perfect_squares(number: int) -> int:\n    \"\"\"Find the minimum count of perfect squares that sum to number.\n\n    Args:\n        number: A positive integer.\n\n    Returns:\n        An integer between 1 and 4 representing the minimum count.\n\n    Examples:\n        >>> num_perfect_squares(9)\n        1\n        >>> num_perfect_squares(10)\n        2\n        >>> num_perfect_squares(12)\n        3\n        >>> num_perfect_squares(31)\n        4\n    \"\"\"\n    if int(math.sqrt(number)) ** 2 == number:\n        return 1\n\n    while number > 0 and number % 4 == 0:\n        number /= 4\n\n    if number % 8 == 7:\n        return 4\n\n    for i in range(1, int(math.sqrt(number)) + 1):\n        if int(math.sqrt(number - i**2)) ** 2 == number - i**2:\n            return 2\n\n    return 3\n"
  },
  {
    "path": "algorithms/math/polynomial.py",
    "content": "\"\"\"\nPolynomial and Monomial Arithmetic\n\nA symbolic algebra system for polynomials and monomials supporting addition,\nsubtraction, multiplication, division, substitution, and polynomial long\ndivision with Fraction-based exact arithmetic.\n\nReference: https://en.wikipedia.org/wiki/Polynomial\n\nComplexity:\n    Time:  Varies by operation\n    Space: O(number of monomials)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Iterable\nfrom fractions import Fraction\nfrom functools import reduce\nfrom numbers import Rational\n\n\nclass Monomial:\n    \"\"\"A monomial represented by a coefficient and variable-to-power mapping.\"\"\"\n\n    def __init__(\n        self, variables: dict[int, int], coeff: int | float | Fraction | None = None\n    ) -> None:\n        \"\"\"Create a monomial with the given variables and coefficient.\n\n        Args:\n            variables: Dictionary mapping variable indices to their powers.\n            coeff: The coefficient (defaults to 0 if empty, 1 otherwise).\n\n        Examples:\n            >>> Monomial({1: 1})  # (a_1)^1\n            >>> Monomial({1: 3, 2: 2}, 12)  # 12(a_1)^3(a_2)^2\n        \"\"\"\n        self.variables = dict()\n\n        if coeff is None:\n            coeff = Fraction(0, 1) if len(variables) == 0 else Fraction(1, 1)\n        elif coeff == 0:\n            self.coeff = Fraction(0, 1)\n            return\n\n        if len(variables) == 0:\n            self.coeff = Monomial._rationalize_if_possible(coeff)\n            return\n\n        for i in variables:\n            if variables[i] != 0:\n                self.variables[i] = variables[i]\n        self.coeff = Monomial._rationalize_if_possible(coeff)\n\n    @staticmethod\n    def _rationalize_if_possible(\n        num: int | float | Fraction,\n    ) -> Fraction | float:\n        \"\"\"Convert numbers to Fraction when possible.\n\n        Args:\n            num: A numeric value.\n\n        Returns:\n            A Fraction if the input is Rational, otherwise the original value.\n        \"\"\"\n        if isinstance(num, Rational):\n            res = Fraction(num, 1)\n            return Fraction(res.numerator, res.denominator)\n        else:\n            return num\n\n    def equal_upto_scalar(self, other: object) -> bool:\n        \"\"\"Check if other is a monomial equivalent to self up to scalar multiple.\n\n        Args:\n            other: Another Monomial to compare.\n\n        Returns:\n            True if both have the same variables with the same powers.\n\n        Raises:\n            ValueError: If other is not a Monomial.\n        \"\"\"\n        if not isinstance(other, Monomial):\n            raise ValueError(\"Can only compare monomials.\")\n        return other.variables == self.variables\n\n    def __add__(self, other: int | float | Fraction) -> Monomial:\n        \"\"\"Add two monomials or a monomial with a scalar.\n\n        Args:\n            other: A Monomial, int, float, or Fraction to add.\n\n        Returns:\n            The resulting Monomial.\n\n        Raises:\n            ValueError: If monomials have different variables.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            return self.__add__(Monomial({}, Monomial._rationalize_if_possible(other)))\n\n        if not isinstance(other, Monomial):\n            raise ValueError(\"Can only add monomials, ints, floats, or Fractions.\")\n\n        if self.variables == other.variables:\n            mono = {i: self.variables[i] for i in self.variables}\n            return Monomial(\n                mono, Monomial._rationalize_if_possible(self.coeff + other.coeff)\n            ).clean()\n\n        raise ValueError(\n            f\"Cannot add {str(other)} to {self.__str__()} \"\n            \"because they don't have same variables.\"\n        )\n\n    def __eq__(self, other: object) -> bool:\n        \"\"\"Check equality of two monomials.\n\n        Args:\n            other: Another Monomial to compare.\n\n        Returns:\n            True if both monomials are equal.\n        \"\"\"\n        if not isinstance(other, Monomial):\n            return NotImplemented\n        return self.equal_upto_scalar(other) and self.coeff == other.coeff\n\n    def __mul__(self, other: int | float | Fraction) -> Monomial:\n        \"\"\"Multiply two monomials or a monomial with a scalar.\n\n        Args:\n            other: A Monomial, int, float, or Fraction to multiply.\n\n        Returns:\n            The resulting Monomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (float, int, Fraction)):\n            mono = {i: self.variables[i] for i in self.variables}\n            return Monomial(\n                mono, Monomial._rationalize_if_possible(self.coeff * other)\n            ).clean()\n\n        if not isinstance(other, Monomial):\n            raise ValueError(\"Can only multiply monomials, ints, floats, or Fractions.\")\n        else:\n            mono = {i: self.variables[i] for i in self.variables}\n            for i in other.variables:\n                if i in mono:\n                    mono[i] += other.variables[i]\n                else:\n                    mono[i] = other.variables[i]\n\n            temp = dict()\n            for k in mono:\n                if mono[k] != 0:\n                    temp[k] = mono[k]\n\n            return Monomial(\n                temp, Monomial._rationalize_if_possible(self.coeff * other.coeff)\n            ).clean()\n\n    def inverse(self) -> Monomial:\n        \"\"\"Compute the multiplicative inverse of this monomial.\n\n        Returns:\n            The inverse Monomial.\n\n        Raises:\n            ValueError: If the coefficient is zero.\n        \"\"\"\n        mono = {i: self.variables[i] for i in self.variables if self.variables[i] != 0}\n        for i in mono:\n            mono[i] *= -1\n        if self.coeff == 0:\n            raise ValueError(\"Coefficient must not be 0.\")\n        return Monomial(mono, Monomial._rationalize_if_possible(1 / self.coeff)).clean()\n\n    def __truediv__(self, other: int | float | Fraction) -> Monomial:\n        \"\"\"Divide this monomial by another monomial or scalar.\n\n        Args:\n            other: A Monomial, int, float, or Fraction divisor.\n\n        Returns:\n            The resulting Monomial.\n\n        Raises:\n            ValueError: If dividing by zero.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            mono = {i: self.variables[i] for i in self.variables}\n            if other == 0:\n                raise ValueError(\"Cannot divide by 0.\")\n            return Monomial(\n                mono, Monomial._rationalize_if_possible(self.coeff / other)\n            ).clean()\n\n        o = other.inverse()\n        return self.__mul__(o)\n\n    def __floordiv__(self, other: int | float | Fraction) -> Monomial:\n        \"\"\"Floor division (same as true division for monomials).\n\n        Args:\n            other: A Monomial, int, float, or Fraction divisor.\n\n        Returns:\n            The resulting Monomial.\n        \"\"\"\n        return self.__truediv__(other)\n\n    def clone(self) -> Monomial:\n        \"\"\"Create a deep copy of this monomial.\n\n        Returns:\n            A new Monomial with the same variables and coefficient.\n        \"\"\"\n        temp_variables = {i: self.variables[i] for i in self.variables}\n        return Monomial(\n            temp_variables, Monomial._rationalize_if_possible(self.coeff)\n        ).clean()\n\n    def clean(self) -> Monomial:\n        \"\"\"Remove variables with zero power.\n\n        Returns:\n            A cleaned Monomial.\n        \"\"\"\n        temp_variables = {\n            i: self.variables[i] for i in self.variables if self.variables[i] != 0\n        }\n        return Monomial(temp_variables, Monomial._rationalize_if_possible(self.coeff))\n\n    def __sub__(self, other: int | float | Fraction) -> Monomial:\n        \"\"\"Subtract a value from this monomial.\n\n        Args:\n            other: A Monomial, int, float, or Fraction to subtract.\n\n        Returns:\n            The resulting Monomial.\n\n        Raises:\n            ValueError: If monomials have different variables.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            mono = {\n                i: self.variables[i] for i in self.variables if self.variables[i] != 0\n            }\n            if len(mono) != 0:\n                raise ValueError(\"Can only subtract like monomials.\")\n            other_term = Monomial(mono, Monomial._rationalize_if_possible(other))\n            return self.__sub__(other_term)\n        if not isinstance(other, Monomial):\n            raise ValueError(\"Can only subtract monomials\")\n        return self.__add__(other.__mul__(Fraction(-1, 1)))\n\n    def __hash__(self) -> int:\n        \"\"\"Hash based on the underlying variables.\n\n        Returns:\n            An integer hash value.\n        \"\"\"\n        arr = []\n        for i in sorted(self.variables):\n            if self.variables[i] > 0:\n                for _ in range(self.variables[i]):\n                    arr.append(i)\n        return hash(tuple(arr))\n\n    def all_variables(self) -> set:\n        \"\"\"Get the set of all variable indices in this monomial.\n\n        Returns:\n            A set of variable indices.\n        \"\"\"\n        return set(sorted(self.variables.keys()))\n\n    def substitute(\n        self,\n        substitutions: int | float | Fraction | dict[int, int | float | Fraction],\n    ) -> Fraction:\n        \"\"\"Evaluate the monomial by substituting values for variables.\n\n        Args:\n            substitutions: A single value applied to all variables, or a\n                dict mapping variable indices to values.\n\n        Returns:\n            The evaluated result.\n\n        Raises:\n            ValueError: If some variables are not given values.\n        \"\"\"\n        if isinstance(substitutions, (int, float, Fraction)):\n            substitutions = {\n                v: Monomial._rationalize_if_possible(substitutions)\n                for v in self.all_variables()\n            }\n        else:\n            if not self.all_variables().issubset(set(substitutions.keys())):\n                raise ValueError(\"Some variables didn't receive their values.\")\n        if self.coeff == 0:\n            return Fraction(0, 1)\n        ans = Monomial._rationalize_if_possible(self.coeff)\n        for k in self.variables:\n            ans *= Monomial._rationalize_if_possible(\n                substitutions[k] ** self.variables[k]\n            )\n        return Monomial._rationalize_if_possible(ans)\n\n    def __str__(self) -> str:\n        \"\"\"Get a string representation of the monomial.\n\n        Returns:\n            A human-readable string.\n        \"\"\"\n        if len(self.variables) == 0:\n            return str(self.coeff)\n\n        result = str(self.coeff)\n        result += \"(\"\n        for i in self.variables:\n            temp = f\"a_{str(i)}\"\n            if self.variables[i] > 1:\n                temp = \"(\" + temp + f\")**{self.variables[i]}\"\n            elif self.variables[i] < 0:\n                temp = \"(\" + temp + f\")**(-{-self.variables[i]})\"\n            elif self.variables[i] == 0:\n                continue\n            else:\n                temp = \"(\" + temp + \")\"\n            result += temp\n        return result + \")\"\n\n\nclass Polynomial:\n    \"\"\"A polynomial represented as a set of Monomial terms.\"\"\"\n\n    def __init__(\n        self, monomials: Iterable[int | float | Fraction | Monomial]\n    ) -> None:\n        \"\"\"Create a polynomial from an iterable of monomials or scalars.\n\n        Args:\n            monomials: An iterable of Monomial, int, float, or Fraction values.\n\n        Raises:\n            ValueError: If an element is not a valid type.\n        \"\"\"\n        self.monomials: set = set()\n        for m in monomials:\n            if any(map(lambda x: isinstance(m, x), [int, float, Fraction])):\n                self.monomials |= {Monomial({}, m)}\n            elif isinstance(m, Monomial):\n                self.monomials |= {m}\n            else:\n                raise ValueError(\n                    \"Iterable should have monomials, int, float, or Fraction.\"\n                )\n        self.monomials -= {Monomial({}, 0)}\n\n    @staticmethod\n    def _rationalize_if_possible(\n        num: int | float | Fraction,\n    ) -> Fraction | float:\n        \"\"\"Convert numbers to Fraction when possible.\n\n        Args:\n            num: A numeric value.\n\n        Returns:\n            A Fraction if the input is Rational, otherwise the original value.\n        \"\"\"\n        if isinstance(num, Rational):\n            res = Fraction(num, 1)\n            return Fraction(res.numerator, res.denominator)\n        else:\n            return num\n\n    def __add__(self, other: int | float | Fraction | Monomial) -> Polynomial:\n        \"\"\"Add a polynomial, monomial, or scalar to this polynomial.\n\n        Args:\n            other: Value to add.\n\n        Returns:\n            The resulting Polynomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            return self.__add__(\n                Monomial({}, Polynomial._rationalize_if_possible(other))\n            )\n        elif isinstance(other, Monomial):\n            monos = {m.clone() for m in self.monomials}\n\n            for _own_monos in monos:\n                if _own_monos.equal_upto_scalar(other):\n                    scalar = _own_monos.coeff\n                    monos -= {_own_monos}\n                    temp_variables = {i: other.variables[i] for i in other.variables}\n                    monos |= {\n                        Monomial(\n                            temp_variables,\n                            Polynomial._rationalize_if_possible(scalar + other.coeff),\n                        )\n                    }\n                    return Polynomial([z for z in monos])\n\n            monos |= {other.clone()}\n            return Polynomial([z for z in monos])\n        elif isinstance(other, Polynomial):\n            temp = list(z for z in {m.clone() for m in self.all_monomials()})\n\n            p = Polynomial(temp)\n            for o in other.all_monomials():\n                p = p.__add__(o.clone())\n            return p\n        else:\n            raise ValueError(\n                \"Can only add int, float, Fraction, Monomials, \"\n                \"or Polynomials to Polynomials.\"\n            )\n\n    def __sub__(self, other: int | float | Fraction | Monomial) -> Polynomial:\n        \"\"\"Subtract a polynomial, monomial, or scalar from this polynomial.\n\n        Args:\n            other: Value to subtract.\n\n        Returns:\n            The resulting Polynomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            return self.__sub__(\n                Monomial({}, Polynomial._rationalize_if_possible(other))\n            )\n        elif isinstance(other, Monomial):\n            monos = {m.clone() for m in self.all_monomials()}\n            for _own_monos in monos:\n                if _own_monos.equal_upto_scalar(other):\n                    scalar = _own_monos.coeff\n                    monos -= {_own_monos}\n                    temp_variables = {i: other.variables[i] for i in other.variables}\n                    monos |= {\n                        Monomial(\n                            temp_variables,\n                            Polynomial._rationalize_if_possible(scalar - other.coeff),\n                        )\n                    }\n                    return Polynomial([z for z in monos])\n\n            to_insert = other.clone()\n            to_insert.coeff *= -1\n\n            monos |= {to_insert}\n            return Polynomial([z for z in monos])\n\n        elif isinstance(other, Polynomial):\n            p = Polynomial(list(z for z in {m.clone() for m in self.all_monomials()}))\n            for o in other.all_monomials():\n                p = p.__sub__(o.clone())\n            return p\n\n        else:\n            raise ValueError(\n                \"Can only subtract int, float, Fraction, \"\n                \"Monomials, or Polynomials from Polynomials.\"\n            )\n\n    def __mul__(self, other: int | float | Fraction | Monomial) -> Polynomial:\n        \"\"\"Multiply this polynomial by another polynomial, monomial, or scalar.\n\n        Args:\n            other: Value to multiply by.\n\n        Returns:\n            The resulting Polynomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction, Monomial)):\n            result = Polynomial([])\n            monos = {m.clone() for m in self.all_monomials()}\n            for m in monos:\n                result = result.__add__(m.clone() * other)\n            return result\n        elif isinstance(other, Polynomial):\n            temp_self = {m.clone() for m in self.all_monomials()}\n            temp_other = {m.clone() for m in other.all_monomials()}\n\n            result = Polynomial([])\n\n            for i in temp_self:\n                for j in temp_other:\n                    result = result.__add__(i * j)\n\n            return result\n        else:\n            raise ValueError(\n                \"Can only multiple int, float, Fraction, \"\n                \"Monomials, or Polynomials with Polynomials.\"\n            )\n\n    def __floordiv__(self, other: int | float | Fraction | Monomial) -> Polynomial:\n        \"\"\"Floor division (same as true division for polynomials).\n\n        Args:\n            other: Divisor value.\n\n        Returns:\n            The resulting Polynomial.\n        \"\"\"\n        return self.__truediv__(other)\n\n    def __truediv__(self, other: int | float | Fraction | Monomial) -> Polynomial:\n        \"\"\"Divide this polynomial by another value.\n\n        Args:\n            other: Divisor (int, float, Fraction, Monomial, or Polynomial).\n\n        Returns:\n            The quotient Polynomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            return self.__truediv__(Monomial({}, other))\n        elif isinstance(other, Monomial):\n            poly_temp = reduce(\n                lambda acc, val: acc + val,\n                map(lambda x: x / other, [z for z in self.all_monomials()]),\n                Polynomial([Monomial({}, 0)]),\n            )\n            return poly_temp\n        elif isinstance(other, Polynomial):\n            quotient, remainder = self.poly_long_division(other)\n            return quotient\n\n        raise ValueError(\n            \"Can only divide a polynomial by an int, float, \"\n            \"Fraction, Monomial, or Polynomial.\"\n        )\n\n    def clone(self) -> Polynomial:\n        \"\"\"Create a deep copy of this polynomial.\n\n        Returns:\n            A new Polynomial with cloned monomials.\n        \"\"\"\n        return Polynomial(list({m.clone() for m in self.all_monomials()}))\n\n    def variables(self) -> set:\n        \"\"\"Get all variable indices present in this polynomial.\n\n        Returns:\n            A set of variable indices.\n        \"\"\"\n        res = set()\n        for i in self.all_monomials():\n            res |= {j for j in i.variables}\n        res = list(res)\n        return set(res)\n\n    def all_monomials(self) -> Iterable[Monomial]:\n        \"\"\"Get all non-zero monomials in this polynomial.\n\n        Returns:\n            A set of Monomial terms.\n        \"\"\"\n        return {m for m in self.monomials if m != Monomial({}, 0)}\n\n    def __eq__(self, other: object) -> bool:\n        \"\"\"Check equality of two polynomials.\n\n        Args:\n            other: Another Polynomial, Monomial, or scalar.\n\n        Returns:\n            True if both represent the same polynomial.\n\n        Raises:\n            ValueError: If other is not a valid type.\n        \"\"\"\n        if isinstance(other, (int, float, Fraction)):\n            other_poly = Polynomial([Monomial({}, other)])\n            return self.__eq__(other_poly)\n        elif isinstance(other, Monomial):\n            return self.__eq__(Polynomial([other]))\n        elif isinstance(other, Polynomial):\n            return self.all_monomials() == other.all_monomials()\n        else:\n            raise ValueError(\n                \"Can only compare a polynomial with an int, \"\n                \"float, Fraction, Monomial, or another Polynomial.\"\n            )\n\n    def subs(\n        self,\n        substitutions: int | float | Fraction | dict[int, int | float | Fraction],\n    ) -> int | float | Fraction:\n        \"\"\"Evaluate the polynomial by substituting values for variables.\n\n        Args:\n            substitutions: A single value applied to all variables, or a\n                dict mapping variable indices to values.\n\n        Returns:\n            The evaluated result.\n\n        Raises:\n            ValueError: If some variables are not given values.\n        \"\"\"\n        if isinstance(substitutions, (int, float, Fraction)):\n            substitutions = {\n                i: Polynomial._rationalize_if_possible(substitutions)\n                for i in set(self.variables())\n            }\n            return self.subs(substitutions)\n        elif not isinstance(substitutions, dict):\n            raise ValueError(\"The substitutions should be a dictionary.\")\n        if not self.variables().issubset(set(substitutions.keys())):\n            raise ValueError(\"Some variables didn't receive their values.\")\n\n        ans = 0\n        for m in self.all_monomials():\n            ans += Polynomial._rationalize_if_possible(m.substitute(substitutions))\n        return Polynomial._rationalize_if_possible(ans)\n\n    def __str__(self) -> str:\n        \"\"\"Get a formatted string representation of the polynomial.\n\n        Returns:\n            A human-readable string.\n        \"\"\"\n        sorted_monos = sorted(\n            self.all_monomials(),\n            key=lambda m: sorted(m.variables.items(), reverse=True),\n            reverse=True,\n        )\n        return \" + \".join(str(m) for m in sorted_monos if m.coeff != Fraction(0, 1))\n\n    def poly_long_division(self, other: Polynomial) -> tuple[Polynomial, Polynomial]:\n        \"\"\"Perform polynomial long division.\n\n        Args:\n            other: The divisor Polynomial.\n\n        Returns:\n            A tuple (quotient, remainder).\n\n        Raises:\n            ValueError: If other is not a Polynomial or is zero.\n        \"\"\"\n        if not isinstance(other, Polynomial):\n            raise ValueError(\"Can only divide by another Polynomial.\")\n\n        if len(other.all_monomials()) == 0:\n            raise ValueError(\"Cannot divide by zero polynomial.\")\n\n        quotient = Polynomial([])\n        remainder = self.clone()\n\n        divisor_monos = sorted(\n            other.all_monomials(),\n            key=lambda m: sorted(m.variables.items(), reverse=True),\n            reverse=True,\n        )\n        divisor_lead = divisor_monos[0]\n\n        while remainder.all_monomials() and max(\n            remainder.variables(), default=-1\n        ) >= max(other.variables(), default=-1):\n            remainder_monos = sorted(\n                remainder.all_monomials(),\n                key=lambda m: sorted(m.variables.items(), reverse=True),\n                reverse=True,\n            )\n            remainder_lead = remainder_monos[0]\n\n            if not all(\n                remainder_lead.variables.get(var, 0)\n                >= divisor_lead.variables.get(var, 0)\n                for var in divisor_lead.variables\n            ):\n                break\n\n            lead_quotient = remainder_lead / divisor_lead\n            quotient = quotient + Polynomial([lead_quotient])\n\n            remainder = remainder - (Polynomial([lead_quotient]) * other)\n\n        return quotient, remainder\n"
  },
  {
    "path": "algorithms/math/polynomial_division.py",
    "content": "\"\"\"Polynomial long division.\n\nDivide polynomial *dividend* by *divisor* and return (quotient, remainder).\nPolynomials are represented as lists of coefficients from highest to lowest\ndegree.  E.g. [1, -3, 2] represents x^2 - 3x + 2.\n\nInspired by PR #840 (KTH-Software-Engineering-DD2480).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef polynomial_division(\n    dividend: list[float], divisor: list[float]\n) -> tuple[list[float], list[float]]:\n    \"\"\"Perform polynomial long division.\n\n    Returns (quotient_coefficients, remainder_coefficients).\n\n    >>> polynomial_division([1, -3, 2], [1, -1])\n    ([1.0, -2.0], [0.0])\n    >>> polynomial_division([1, 0, -4], [1, -2])\n    ([1.0, 2.0], [0.0])\n    \"\"\"\n    if not divisor or all(c == 0 for c in divisor):\n        msg = \"Cannot divide by zero polynomial\"\n        raise ZeroDivisionError(msg)\n    dividend = [float(c) for c in dividend]\n    divisor = [float(c) for c in divisor]\n    remainder = list(dividend)\n    quotient: list[float] = []\n    divisor_lead = divisor[0]\n    len_diff = len(dividend) - len(divisor)\n    for i in range(len_diff + 1):\n        coeff = remainder[i] / divisor_lead\n        quotient.append(coeff)\n        for j in range(len(divisor)):\n            remainder[i + j] -= coeff * divisor[j]\n    # The remainder is the tail of the array\n    remainder = remainder[len_diff + 1 :]\n    if not remainder:\n        remainder = [0.0]\n    return quotient, remainder\n"
  },
  {
    "path": "algorithms/math/power.py",
    "content": "\"\"\"\nBinary Exponentiation\n\nCompute a^n efficiently using binary exponentiation (exponentiation by\nsquaring), with optional modular arithmetic.\n\nReference: https://en.wikipedia.org/wiki/Exponentiation_by_squaring\n\nComplexity:\n    Time:  O(log n)\n    Space: O(1) iterative, O(log n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef power(a: int, n: int, mod: int | None = None) -> int:\n    \"\"\"Compute a^n iteratively using binary exponentiation.\n\n    Args:\n        a: The base.\n        n: The exponent.\n        mod: Optional modulus for modular exponentiation.\n\n    Returns:\n        a^n, or a^n % mod if mod is specified.\n\n    Examples:\n        >>> power(2, 3)\n        8\n        >>> power(10, 3, 5)\n        0\n    \"\"\"\n    ans = 1\n    while n:\n        if n & 1:\n            ans = ans * a\n        a = a * a\n        if mod:\n            ans %= mod\n            a %= mod\n        n >>= 1\n    return ans\n\n\ndef power_recur(a: int, n: int, mod: int | None = None) -> int:\n    \"\"\"Compute a^n recursively using binary exponentiation.\n\n    Args:\n        a: The base.\n        n: The exponent.\n        mod: Optional modulus for modular exponentiation.\n\n    Returns:\n        a^n, or a^n % mod if mod is specified.\n\n    Examples:\n        >>> power_recur(2, 3)\n        8\n    \"\"\"\n    if n == 0:\n        ans = 1\n    elif n == 1:\n        ans = a\n    else:\n        ans = power_recur(a, n // 2, mod)\n        ans = ans * ans\n        if n % 2:\n            ans = ans * a\n    if mod:\n        ans %= mod\n    return ans\n"
  },
  {
    "path": "algorithms/math/prime_check.py",
    "content": "\"\"\"\nPrimality Test\n\nCheck whether a given integer is prime using trial division with 6k +/- 1\noptimization.\n\nReference: https://en.wikipedia.org/wiki/Primality_test\n\nComplexity:\n    Time:  O(sqrt(n))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef prime_check(n: int) -> bool:\n    \"\"\"Check whether n is a prime number.\n\n    Args:\n        n: The integer to test.\n\n    Returns:\n        True if n is prime, False otherwise.\n\n    Examples:\n        >>> prime_check(7)\n        True\n        >>> prime_check(4)\n        False\n    \"\"\"\n    if n <= 1:\n        return False\n    if n == 2 or n == 3:\n        return True\n    if n % 2 == 0 or n % 3 == 0:\n        return False\n    j = 5\n    while j * j <= n:\n        if n % j == 0 or n % (j + 2) == 0:\n            return False\n        j += 6\n    return True\n"
  },
  {
    "path": "algorithms/math/primes_sieve_of_eratosthenes.py",
    "content": "\"\"\"\nSieve of Eratosthenes\n\nGenerate all prime numbers less than n using an optimized sieve that skips\neven numbers.\n\nReference: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes\n\nComplexity:\n    Time:  O(n log log n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef get_primes(n: int) -> list[int]:\n    \"\"\"Return all primes less than n using the Sieve of Eratosthenes.\n\n    Args:\n        n: Upper bound (exclusive). Must be a positive integer.\n\n    Returns:\n        A sorted list of all primes less than n.\n\n    Raises:\n        ValueError: If n is not positive.\n\n    Examples:\n        >>> get_primes(7)\n        [2, 3, 5, 7]\n    \"\"\"\n    if n <= 0:\n        raise ValueError(\"'n' must be a positive integer.\")\n    sieve_size = (n // 2 - 1) if n % 2 == 0 else (n // 2)\n    sieve = [True for _ in range(sieve_size)]\n    primes: list[int] = []\n    if n >= 2:\n        primes.append(2)\n    for i in range(sieve_size):\n        if sieve[i]:\n            value_at_i = i * 2 + 3\n            primes.append(value_at_i)\n            for j in range(i, sieve_size, value_at_i):\n                sieve[j] = False\n    return primes\n"
  },
  {
    "path": "algorithms/math/pythagoras.py",
    "content": "\"\"\"\nPythagorean Theorem\n\nGiven the lengths of two sides of a right-angled triangle, compute the\nlength of the third side using the Pythagorean theorem.\n\nReference: https://en.wikipedia.org/wiki/Pythagorean_theorem\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef pythagoras(\n    opposite: float | str, adjacent: float | str, hypotenuse: float | str\n) -> str:\n    \"\"\"Compute the unknown side of a right triangle.\n\n    Pass \"?\" as the unknown side to calculate it from the other two.\n\n    Args:\n        opposite: Length of the opposite side, or \"?\" if unknown.\n        adjacent: Length of the adjacent side, or \"?\" if unknown.\n        hypotenuse: Length of the hypotenuse, or \"?\" if unknown.\n\n    Returns:\n        A string describing the computed side and its value.\n\n    Raises:\n        ValueError: If the arguments are invalid.\n\n    Examples:\n        >>> pythagoras(3, 4, \"?\")\n        'Hypotenuse = 5.0'\n    \"\"\"\n    try:\n        if opposite == \"?\":\n            return \"Opposite = \" + str(((hypotenuse**2) - (adjacent**2)) ** 0.5)\n        if adjacent == \"?\":\n            return \"Adjacent = \" + str(((hypotenuse**2) - (opposite**2)) ** 0.5)\n        if hypotenuse == \"?\":\n            return \"Hypotenuse = \" + str(((opposite**2) + (adjacent**2)) ** 0.5)\n        return \"You already know the answer!\"\n    except Exception as err:\n        raise ValueError(\"invalid argument(s) were given.\") from err\n"
  },
  {
    "path": "algorithms/math/rabin_miller.py",
    "content": "\"\"\"\nRabin-Miller Primality Test\n\nA probabilistic primality test where returning False guarantees the number\nis composite, and returning True means the number is probably prime with\na 4^(-k) chance of error.\n\nReference: https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test\n\nComplexity:\n    Time:  O(k * log^2(n))\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport secrets\n\n\ndef is_prime(n: int, k: int) -> bool:\n    \"\"\"Test if n is probably prime using the Rabin-Miller algorithm.\n\n    Args:\n        n: The integer to test for primality.\n        k: The number of rounds of testing (higher = more accurate).\n\n    Returns:\n        True if n is probably prime, False if n is definitely composite.\n\n    Examples:\n        >>> is_prime(7, 2)\n        True\n        >>> is_prime(6, 2)\n        False\n    \"\"\"\n\n    def _pow2_factor(num: int) -> tuple[int, int]:\n        \"\"\"Factor num into 2^power * odd_part.\n\n        Args:\n            num: The integer to factor.\n\n        Returns:\n            A tuple (power, odd_part).\n        \"\"\"\n        power = 0\n        while num % 2 == 0:\n            num //= 2\n            power += 1\n        return power, num\n\n    def _valid_witness(a: int) -> bool:\n        \"\"\"Check if a is a witness for the compositeness of n.\n\n        Args:\n            a: The potential witness value.\n\n        Returns:\n            True if a proves n is composite, False otherwise.\n        \"\"\"\n        x = pow(int(a), int(d), int(n))\n\n        if x == 1 or x == n - 1:\n            return False\n\n        for _ in range(r - 1):\n            x = pow(int(x), 2, int(n))\n\n            if x == 1:\n                return True\n            if x == n - 1:\n                return False\n\n        return True\n\n    if n < 5:\n        return n == 2 or n == 3\n\n    r, d = _pow2_factor(n - 1)\n\n    return all(\n        not _valid_witness(secrets.randbelow(n - 4) + 2)\n        for _ in range(k)\n    )\n"
  },
  {
    "path": "algorithms/math/recursive_binomial_coefficient.py",
    "content": "\"\"\"\nRecursive Binomial Coefficient\n\nCalculate the binomial coefficient C(n, k) using a recursive formula with\nthe identity C(n, k) = (n/k) * C(n-1, k-1).\n\nReference: https://en.wikipedia.org/wiki/Binomial_coefficient\n\nComplexity:\n    Time:  O(k)\n    Space: O(k) recursive stack\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef recursive_binomial_coefficient(n: int, k: int) -> int:\n    \"\"\"Calculate C(n, k) recursively, where n >= k.\n\n    Args:\n        n: Total number of items.\n        k: Number of items to choose.\n\n    Returns:\n        The binomial coefficient C(n, k).\n\n    Raises:\n        ValueError: If k > n.\n\n    Examples:\n        >>> recursive_binomial_coefficient(5, 0)\n        1\n        >>> recursive_binomial_coefficient(8, 2)\n        28\n    \"\"\"\n    if k > n:\n        raise ValueError(\"Invalid Inputs, ensure that n >= k\")\n    if k == 0 or n == k:\n        return 1\n    if k > n / 2:\n        return recursive_binomial_coefficient(n, n - k)\n    return int((n / k) * recursive_binomial_coefficient(n - 1, k - 1))\n"
  },
  {
    "path": "algorithms/math/rsa.py",
    "content": "\"\"\"\nRSA Encryption Algorithm\n\nImplements RSA key generation, encryption, and decryption. RSA uses separate\npublic and private keys where ((x^e)^d) % n == x % n.\n\nReference: https://en.wikipedia.org/wiki/RSA_(cryptosystem)\n\nComplexity:\n    Time:  O(k^3) for key generation (k = bit length)\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport secrets\n\n\ndef _extended_gcd(a: int, b: int) -> tuple[int, int, int]:\n    old_r, r = a, b\n    old_s, s = 1, 0\n    old_t, t = 0, 1\n    while r != 0:\n        q = old_r // r\n        old_r, r = r, old_r - q * r\n        old_s, s = s, old_s - q * s\n        old_t, t = t, old_t - q * t\n    return old_r, old_s, old_t\n\n\ndef _modinv(a: int, m: int) -> int:\n    g, x, _ = _extended_gcd(a, m)\n    if g != 1:\n        raise ValueError(f\"Modular inverse does not exist: gcd({a}, {m}) = {g}\")\n    return x % m\n\n\ndef generate_key(k: int, seed: int | None = None) -> tuple[int, int, int]:\n    \"\"\"Generate an RSA key triplet (n, e, d).\n\n    Args:\n        k: The number of bits in the modulus n.\n        seed: Optional random seed for reproducibility\n            (ignored, kept for API compatibility).\n\n    Returns:\n        A tuple (n, e, d) where n is the modulus, e is the encryption\n        exponent, and d is the decryption exponent.\n\n    Examples:\n        >>> n, e, d = generate_key(16)\n    \"\"\"\n\n    def _gen_prime(k: int, seed: int | None = None) -> int:\n        \"\"\"Generate a random prime with k bits.\n\n        Args:\n            k: The number of bits.\n            seed: Unused, kept for API compatibility.\n\n        Returns:\n            A prime number.\n        \"\"\"\n\n        def _is_prime(num: int) -> bool:\n            if num == 2:\n                return True\n            return all(\n                num % i != 0\n                for i in range(2, int(num**0.5) + 1)\n            )\n\n        while True:\n            key = secrets.randbelow(int(2**k) - int(2 ** (k - 1))) + int(2 ** (k - 1))\n            if _is_prime(key):\n                return key\n\n    p_size = k // 2\n    q_size = k - p_size\n\n    e = _gen_prime(k, seed)\n\n    while True:\n        p = _gen_prime(p_size, seed)\n        if p % e != 1:\n            break\n\n    while True:\n        q = _gen_prime(q_size, seed)\n        if q % e != 1:\n            break\n\n    n = p * q\n    totient = (p - 1) * (q - 1)\n    d = _modinv(e, totient)\n\n    return int(n), int(e), int(d)\n\n\ndef encrypt(data: int, e: int, n: int) -> int:\n    \"\"\"Encrypt data using RSA public key.\n\n    Args:\n        data: The plaintext integer.\n        e: The encryption exponent.\n        n: The modulus.\n\n    Returns:\n        The encrypted integer.\n\n    Examples:\n        >>> encrypt(7, 23, 143)\n        2\n    \"\"\"\n    return pow(int(data), int(e), int(n))\n\n\ndef decrypt(data: int, d: int, n: int) -> int:\n    \"\"\"Decrypt data using RSA private key.\n\n    Args:\n        data: The encrypted integer.\n        d: The decryption exponent.\n        n: The modulus.\n\n    Returns:\n        The decrypted plaintext integer.\n\n    Examples:\n        >>> decrypt(2, 47, 143)\n        7\n    \"\"\"\n    return pow(int(data), int(d), int(n))\n"
  },
  {
    "path": "algorithms/math/sqrt_precision_factor.py",
    "content": "\"\"\"\nSquare Root by Newton's Method\n\nCompute the square root of a positive number using Newton's method\n(Babylonian method) with a configurable precision factor.\n\nReference: https://en.wikipedia.org/wiki/Newton%27s_method#Square_root\n\nComplexity:\n    Time:  O(log(1/epsilon)) iterations for convergence\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef square_root(n: float, epsilon: float = 0.001) -> float:\n    \"\"\"Compute the square root of n with maximum absolute error epsilon.\n\n    Args:\n        n: A positive number.\n        epsilon: Maximum allowed absolute error.\n\n    Returns:\n        An approximation of sqrt(n).\n\n    Examples:\n        >>> abs(square_root(5, 0.001) - 2.236) < 0.002\n        True\n    \"\"\"\n    guess = n / 2\n\n    while abs(guess * guess - n) > epsilon:\n        guess = (guess + (n / guess)) / 2\n\n    return guess\n"
  },
  {
    "path": "algorithms/math/summing_digits.py",
    "content": "\"\"\"\nSumming Digits Power\n\nFind all integers in a range where each digit raised to its positional power\n(1-indexed) sums to the number itself (e.g., 89 = 8^1 + 9^2).\n\nReference: https://en.wikipedia.org/wiki/Perfect_digit-to-digit_invariant\n\nComplexity:\n    Time:  O((high - low) * d) where d is the number of digits\n    Space: O(result size)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef sum_dig_pow(low: int, high: int) -> list[int]:\n    \"\"\"Find numbers where digits raised to positional powers equal the number.\n\n    Args:\n        low: Lower bound of the range (inclusive).\n        high: Upper bound of the range (inclusive).\n\n    Returns:\n        List of matching numbers in the range.\n\n    Examples:\n        >>> sum_dig_pow(1, 10)\n        [1, 2, 3, 4, 5, 6, 7, 8, 9]\n        >>> sum_dig_pow(1, 100)\n        [1, 2, 3, 4, 5, 6, 7, 8, 9, 89]\n    \"\"\"\n    result = []\n\n    for number in range(low, high + 1):\n        exponent = 1\n        summation = 0\n        number_as_string = str(number)\n\n        tokens = list(map(int, number_as_string))\n\n        for k in tokens:\n            summation = summation + (k**exponent)\n            exponent += 1\n\n        if summation == number:\n            result.append(number)\n    return result\n"
  },
  {
    "path": "algorithms/math/surface_area_of_torus.py",
    "content": "\"\"\"\nSurface Area of a Torus\n\nCalculate the surface area of a torus given its major and minor radii\nusing the formula A = 4 * pi^2 * R * r.\n\nReference: https://en.wikipedia.org/wiki/Torus\n\nComplexity:\n    Time:  O(1)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom math import pi\n\n\ndef surface_area_of_torus(major_radius: float, minor_radius: float) -> float:\n    \"\"\"Calculate the surface area of a torus.\n\n    Args:\n        major_radius: Distance from the center of the tube to the center\n            of the torus (R).\n        minor_radius: Radius of the tube (r).\n\n    Returns:\n        The surface area of the torus.\n\n    Raises:\n        ValueError: If either radius is negative.\n\n    Examples:\n        >>> surface_area_of_torus(3.0, 1.0)\n        118.4352528130723\n    \"\"\"\n    if major_radius < 0 or minor_radius < 0:\n        raise ValueError(\"Radii must be non-negative\")\n\n    return 4 * pi**2 * major_radius * minor_radius\n"
  },
  {
    "path": "algorithms/math/symmetry_group_cycle_index.py",
    "content": "\"\"\"\nSymmetry Group Cycle Index\n\nCompute the cycle index polynomial of the symmetric group S_n and use it\nto count distinct configurations of grids under row/column permutations\nvia Burnside's lemma.\n\nReference: https://en.wikipedia.org/wiki/Cycle_index#Symmetric_group_Sn\n\nComplexity:\n    Time:  O(n!) for cycle index computation\n    Space: O(n!) for memoization\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom fractions import Fraction\n\nfrom algorithms.math.gcd import lcm\nfrom algorithms.math.polynomial import Monomial, Polynomial\n\n\ndef cycle_product(m1: Monomial, m2: Monomial) -> Monomial:\n    \"\"\"Compute the cycle product of two monomials from cycle indices.\n\n    Args:\n        m1: First monomial from a cycle index.\n        m2: Second monomial from a cycle index.\n\n    Returns:\n        The resultant monomial from the Cartesian product merging.\n    \"\"\"\n    assert isinstance(m1, Monomial) and isinstance(m2, Monomial)\n    a_vars = m1.variables\n    b_vars = m2.variables\n    result_variables: dict[int, int] = dict()\n    for i in a_vars:\n        for j in b_vars:\n            k = lcm(i, j)\n            g = (i * j) // k\n            if k in result_variables:\n                result_variables[k] += a_vars[i] * b_vars[j] * g\n            else:\n                result_variables[k] = a_vars[i] * b_vars[j] * g\n\n    return Monomial(result_variables, Fraction(m1.coeff * m2.coeff, 1))\n\n\ndef cycle_product_for_two_polynomials(\n    p1: Polynomial, p2: Polynomial, q: float | int | Fraction\n) -> float | int | Fraction:\n    \"\"\"Compute the product of two cycle indices and evaluate at q.\n\n    Args:\n        p1: First cycle index polynomial.\n        p2: Second cycle index polynomial.\n        q: The value to substitute for all variables.\n\n    Returns:\n        The evaluated result.\n    \"\"\"\n    ans = Fraction(0, 1)\n    for m1 in p1.monomials:\n        for m2 in p2.monomials:\n            ans += cycle_product(m1, m2).substitute(q)\n\n    return ans\n\n\ndef _cycle_index_sym_helper(n: int, memo: dict[int, Polynomial]) -> Polynomial:\n    \"\"\"Recursively compute the cycle index of S_n with memoization.\n\n    Args:\n        n: The order of the symmetric group.\n        memo: Dictionary of precomputed cycle indices.\n\n    Returns:\n        The cycle index polynomial of S_n.\n    \"\"\"\n    if n in memo:\n        return memo[n]\n    ans = Polynomial([Monomial({}, Fraction(0, 1))])\n    for t in range(1, n + 1):\n        ans = ans.__add__(\n            Polynomial([Monomial({t: 1}, Fraction(1, 1))])\n            * _cycle_index_sym_helper(n - t, memo)\n        )\n    ans *= Fraction(1, n)\n    memo[n] = ans\n    return memo[n]\n\n\ndef get_cycle_index_sym(n: int) -> Polynomial:\n    \"\"\"Compute the cycle index of the symmetric group S_n.\n\n    Args:\n        n: The order of the symmetric group (non-negative integer).\n\n    Returns:\n        The cycle index as a Polynomial.\n\n    Raises:\n        ValueError: If n is negative.\n\n    Examples:\n        >>> get_cycle_index_sym(1)  # doctest: +SKIP\n        Polynomial(...)\n    \"\"\"\n    if n < 0:\n        raise ValueError(\"n should be a non-negative integer.\")\n\n    memo = {\n        0: Polynomial([Monomial({}, Fraction(1, 1))]),\n        1: Polynomial([Monomial({1: 1}, Fraction(1, 1))]),\n        2: Polynomial(\n            [Monomial({1: 2}, Fraction(1, 2)), Monomial({2: 1}, Fraction(1, 2))]\n        ),\n        3: Polynomial(\n            [\n                Monomial({1: 3}, Fraction(1, 6)),\n                Monomial({1: 1, 2: 1}, Fraction(1, 2)),\n                Monomial({3: 1}, Fraction(1, 3)),\n            ]\n        ),\n        4: Polynomial(\n            [\n                Monomial({1: 4}, Fraction(1, 24)),\n                Monomial({2: 1, 1: 2}, Fraction(1, 4)),\n                Monomial({3: 1, 1: 1}, Fraction(1, 3)),\n                Monomial({2: 2}, Fraction(1, 8)),\n                Monomial({4: 1}, Fraction(1, 4)),\n            ]\n        ),\n    }\n    result = _cycle_index_sym_helper(n, memo)\n    return result\n"
  },
  {
    "path": "algorithms/matrix/__init__.py",
    "content": "from algorithms.matrix import (  # noqa: F401\n    bomb_enemy,\n    cholesky_matrix_decomposition,\n    copy_transform,\n    count_paths,\n    crout_matrix_decomposition,\n    matrix_exponentiation,\n    matrix_inversion,\n    multiply,\n    rotate_image,\n    search_in_sorted_matrix,\n    sort_matrix_diagonally,\n    sparse_dot_vector,\n    sparse_mul,\n    spiral_traversal,\n    sudoku_validator,\n    sum_sub_squares,\n)\n\n__all__ = [\n    \"bomb_enemy\",\n    \"cholesky_matrix_decomposition\",\n    \"copy_transform\",\n    \"count_paths\",\n    \"crout_matrix_decomposition\",\n    \"matrix_exponentiation\",\n    \"matrix_inversion\",\n    \"multiply\",\n    \"rotate_image\",\n    \"search_in_sorted_matrix\",\n    \"sort_matrix_diagonally\",\n    \"sparse_dot_vector\",\n    \"sparse_mul\",\n    \"spiral_traversal\",\n    \"sudoku_validator\",\n    \"sum_sub_squares\",\n]\n"
  },
  {
    "path": "algorithms/matrix/bomb_enemy.py",
    "content": "\"\"\"\nBomb Enemy\n\nGiven a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0'\n(the number zero). Return the maximum enemies you can kill using one bomb.\nThe bomb kills all the enemies in the same row and column from the planted\npoint until it hits the wall since it is too strong to be destroyed.\nYou can only place the bomb at an empty cell.\n\nReference: https://leetcode.com/problems/bomb-enemy/\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_killed_enemies(grid: list[list[str]]) -> int:\n    \"\"\"Return the maximum enemies killed by placing one bomb.\n\n    Args:\n        grid: 2D grid of 'W', 'E', and '0' characters.\n\n    Returns:\n        Maximum number of enemies killed.\n\n    Examples:\n        >>> max_killed_enemies([[\"0\",\"E\",\"0\",\"0\"],[\"E\",\"0\",\"W\",\"E\"],[\"0\",\"E\",\"0\",\"0\"]])\n        3\n    \"\"\"\n    if not grid:\n        return 0\n    rows, cols = len(grid), len(grid[0])\n    max_killed = 0\n    row_enemies, col_enemies = 0, [0] * cols\n    for i in range(rows):\n        for j in range(cols):\n            if j == 0 or grid[i][j - 1] == \"W\":\n                row_enemies = _row_kills(grid, i, j)\n            if i == 0 or grid[i - 1][j] == \"W\":\n                col_enemies[j] = _col_kills(grid, i, j)\n            if grid[i][j] == \"0\":\n                max_killed = max(max_killed, row_enemies + col_enemies[j])\n    return max_killed\n\n\ndef _row_kills(grid: list[list[str]], row: int, col: int) -> int:\n    \"\"\"Count enemies killed in a row starting from the given column.\n\n    Args:\n        grid: 2D grid of characters.\n        row: Row index.\n        col: Starting column index.\n\n    Returns:\n        Number of enemies in the row segment.\n    \"\"\"\n    count = 0\n    num_cols = len(grid[0])\n    while col < num_cols and grid[row][col] != \"W\":\n        if grid[row][col] == \"E\":\n            count += 1\n        col += 1\n    return count\n\n\ndef _col_kills(grid: list[list[str]], row: int, col: int) -> int:\n    \"\"\"Count enemies killed in a column starting from the given row.\n\n    Args:\n        grid: 2D grid of characters.\n        row: Starting row index.\n        col: Column index.\n\n    Returns:\n        Number of enemies in the column segment.\n    \"\"\"\n    count = 0\n    num_rows = len(grid)\n    while row < num_rows and grid[row][col] != \"W\":\n        if grid[row][col] == \"E\":\n            count += 1\n        row += 1\n    return count\n"
  },
  {
    "path": "algorithms/matrix/cholesky_matrix_decomposition.py",
    "content": "\"\"\"\nCholesky Matrix Decomposition\n\nDecompose a Hermitian positive-definite matrix A into a lower-triangular\nmatrix V such that V * V^T = A. Mainly used for numerical solution of\nlinear equations Ax = b.\n\nReference: https://en.wikipedia.org/wiki/Cholesky_decomposition\n\nComplexity:\n    Time:  O(n^3)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef cholesky_decomposition(matrix: list[list[float]]) -> list[list[float]] | None:\n    \"\"\"Compute the Cholesky decomposition of a positive-definite matrix.\n\n    Args:\n        matrix: Hermitian positive-definite matrix (n x n).\n\n    Returns:\n        Lower-triangular matrix V such that V * V^T = matrix,\n        or None if the matrix cannot be decomposed.\n\n    Examples:\n        >>> cholesky_decomposition([[4, 12, -16], [12, 37, -43], [-16, -43, 98]])\n        [[2.0, 0.0, 0.0], [6.0, 1.0, 0.0], [-8.0, 5.0, 3.0]]\n    \"\"\"\n    size = len(matrix)\n    for row in matrix:\n        if len(row) != size:\n            return None\n    result = [[0.0] * size for _ in range(size)]\n    for j in range(size):\n        diagonal_sum = sum(result[j][k] ** 2 for k in range(j))\n        diagonal_sum = matrix[j][j] - diagonal_sum\n        if diagonal_sum <= 0:\n            return None\n        result[j][j] = math.sqrt(diagonal_sum)\n        for i in range(j + 1, size):\n            off_diagonal_sum = sum(result[i][k] * result[j][k] for k in range(j))\n            result[i][j] = (matrix[i][j] - off_diagonal_sum) / result[j][j]\n    return result\n"
  },
  {
    "path": "algorithms/matrix/copy_transform.py",
    "content": "\"\"\"\nCopy Transform\n\nRotate and invert a matrix by creating transformed copies.\nProvides clockwise rotation, counterclockwise rotation, top-left\ninversion (transpose), and bottom-left inversion (anti-transpose).\n\nReference: https://en.wikipedia.org/wiki/Transpose\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef rotate_clockwise(matrix: list[list[int]]) -> list[list[int]]:\n    \"\"\"Rotate a matrix 90 degrees clockwise via copy.\n\n    Args:\n        matrix: 2D list of integers.\n\n    Returns:\n        New matrix rotated 90 degrees clockwise.\n\n    Examples:\n        >>> rotate_clockwise([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [[7, 4, 1], [8, 5, 2], [9, 6, 3]]\n    \"\"\"\n    result: list[list[int]] = []\n    for row in reversed(matrix):\n        for i, elem in enumerate(row):\n            try:\n                result[i].append(elem)\n            except IndexError:\n                result.insert(i, [])\n                result[i].append(elem)\n    return result\n\n\ndef rotate_counterclockwise(matrix: list[list[int]]) -> list[list[int]]:\n    \"\"\"Rotate a matrix 90 degrees counterclockwise via copy.\n\n    Args:\n        matrix: 2D list of integers.\n\n    Returns:\n        New matrix rotated 90 degrees counterclockwise.\n\n    Examples:\n        >>> rotate_counterclockwise([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [[3, 6, 9], [2, 5, 8], [1, 4, 7]]\n    \"\"\"\n    result: list[list[int]] = []\n    for row in matrix:\n        for i, elem in enumerate(reversed(row)):\n            try:\n                result[i].append(elem)\n            except IndexError:\n                result.insert(i, [])\n                result[i].append(elem)\n    return result\n\n\ndef top_left_invert(matrix: list[list[int]]) -> list[list[int]]:\n    \"\"\"Transpose a matrix (reflect over the main diagonal).\n\n    Args:\n        matrix: 2D list of integers.\n\n    Returns:\n        Transposed matrix.\n\n    Examples:\n        >>> top_left_invert([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [[1, 4, 7], [2, 5, 8], [3, 6, 9]]\n    \"\"\"\n    result: list[list[int]] = []\n    for row in matrix:\n        for i, elem in enumerate(row):\n            try:\n                result[i].append(elem)\n            except IndexError:\n                result.insert(i, [])\n                result[i].append(elem)\n    return result\n\n\ndef bottom_left_invert(matrix: list[list[int]]) -> list[list[int]]:\n    \"\"\"Anti-transpose a matrix (reflect over the anti-diagonal).\n\n    Args:\n        matrix: 2D list of integers.\n\n    Returns:\n        Anti-transposed matrix.\n\n    Examples:\n        >>> bottom_left_invert([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [[9, 6, 3], [8, 5, 2], [7, 4, 1]]\n    \"\"\"\n    result: list[list[int]] = []\n    for row in reversed(matrix):\n        for i, elem in enumerate(reversed(row)):\n            try:\n                result[i].append(elem)\n            except IndexError:\n                result.insert(i, [])\n                result[i].append(elem)\n    return result\n"
  },
  {
    "path": "algorithms/matrix/count_paths.py",
    "content": "\"\"\"\nCount Paths\n\nCount the number of unique paths from the top-left corner to the\nbottom-right corner of an m x n grid. Movement is restricted to\nright or down only. Uses dynamic programming.\n\nReference: https://leetcode.com/problems/unique-paths/\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count_paths(rows: int, cols: int) -> int:\n    \"\"\"Count unique paths from top-left to bottom-right of an m x n grid.\n\n    Args:\n        rows: Number of rows in the grid.\n        cols: Number of columns in the grid.\n\n    Returns:\n        Number of unique paths, or -1 if dimensions are invalid.\n\n    Examples:\n        >>> count_paths(3, 3)\n        6\n    \"\"\"\n    if rows < 1 or cols < 1:\n        return -1\n    count = [[None for _ in range(cols)] for _ in range(rows)]\n\n    for i in range(cols):\n        count[0][i] = 1\n    for j in range(rows):\n        count[j][0] = 1\n\n    for i in range(1, rows):\n        for j in range(1, cols):\n            count[i][j] = count[i - 1][j] + count[i][j - 1]\n\n    return count[rows - 1][cols - 1]\n"
  },
  {
    "path": "algorithms/matrix/crout_matrix_decomposition.py",
    "content": "\"\"\"\nCrout Matrix Decomposition\n\nDecompose a matrix A into lower-triangular matrix L and upper-triangular\nmatrix U such that L * U = A. L has non-zero elements only on and below\nthe diagonal; U has non-zero elements only on and above the diagonal\nwith ones on the diagonal.\n\nReference: https://en.wikipedia.org/wiki/Crout_matrix_decomposition\n\nComplexity:\n    Time:  O(n^3)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef crout_matrix_decomposition(\n    matrix: list[list[float]],\n) -> tuple[list[list[float]], list[list[float]]]:\n    \"\"\"Perform Crout decomposition of a square matrix.\n\n    Args:\n        matrix: Square matrix of size n x n.\n\n    Returns:\n        Tuple (L, U) of lower and upper triangular matrices.\n\n    Examples:\n        >>> crout_matrix_decomposition([[9, 9], [7, 7]])\n        ([[9.0, 0.0], [7.0, 0.0]], [[1.0, 1.0], [0.0, 1.0]])\n    \"\"\"\n    size = len(matrix)\n    lower = [[0.0] * size for _ in range(size)]\n    upper = [[0.0] * size for _ in range(size)]\n    for j in range(size):\n        upper[j][j] = 1.0\n        for i in range(j, size):\n            alpha = float(matrix[i][j])\n            for k in range(j):\n                alpha -= lower[i][k] * upper[k][j]\n            lower[i][j] = float(alpha)\n        for i in range(j + 1, size):\n            temp = float(matrix[j][i])\n            for k in range(j):\n                temp -= float(lower[j][k] * upper[k][i])\n            if int(lower[j][j]) == 0:\n                lower[j][j] = float(0.1**40)\n            upper[j][i] = float(temp / lower[j][j])\n    return (lower, upper)\n"
  },
  {
    "path": "algorithms/matrix/matrix_exponentiation.py",
    "content": "\"\"\"\nMatrix Exponentiation\n\nCompute the n-th power of a square matrix using repeated squaring\n(exponentiation by squaring). Useful for computing Fibonacci numbers,\nlinear recurrences, and graph path counting.\n\nReference: https://en.wikipedia.org/wiki/Exponentiation_by_squaring\n\nComplexity:\n    Time:  O(d^3 * log n)  where d is the matrix dimension\n    Space: O(d^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef multiply(mat_a: list[list[int]], mat_b: list[list[int]]) -> list[list[int]]:\n    \"\"\"Multiply two square matrices.\n\n    Args:\n        mat_a: First square matrix (n x n).\n        mat_b: Second square matrix (n x n).\n\n    Returns:\n        Product matrix of mat_a and mat_b.\n\n    Examples:\n        >>> multiply([[1, 0], [0, 1]], [[2, 3], [4, 5]])\n        [[2, 3], [4, 5]]\n    \"\"\"\n    size = len(mat_a)\n    result = [[0] * size for _ in range(size)]\n    for i in range(size):\n        for j in range(size):\n            for k in range(size):\n                result[i][j] += mat_a[i][k] * mat_b[k][j]\n    return result\n\n\ndef identity(size: int) -> list[list[int]]:\n    \"\"\"Return the identity matrix of the given size.\n\n    Args:\n        size: Dimension of the identity matrix.\n\n    Returns:\n        Identity matrix of size n x n.\n\n    Examples:\n        >>> identity(3)\n        [[1, 0, 0], [0, 1, 0], [0, 0, 1]]\n    \"\"\"\n    result = [[0] * size for _ in range(size)]\n    for i in range(size):\n        result[i][i] = 1\n    return result\n\n\ndef matrix_exponentiation(mat: list[list[int]], power: int) -> list[list[int]]:\n    \"\"\"Compute mat raised to the given power by repeated squaring.\n\n    Args:\n        mat: Square matrix to exponentiate.\n        power: Non-negative integer exponent.\n\n    Returns:\n        Matrix mat^power.\n\n    Examples:\n        >>> matrix_exponentiation([[1, 0], [0, 1]], 5)\n        [[1, 0], [0, 1]]\n    \"\"\"\n    if power == 0:\n        return identity(len(mat))\n    elif power % 2 == 1:\n        return multiply(matrix_exponentiation(mat, power - 1), mat)\n    else:\n        half = matrix_exponentiation(mat, power // 2)\n        return multiply(half, half)\n"
  },
  {
    "path": "algorithms/matrix/matrix_inversion.py",
    "content": "\"\"\"\nMatrix Inversion\n\nCompute the inverse of an invertible n x n matrix A, returning an n x n\nmatrix B such that A * B = B * A = I (the identity matrix). Uses cofactor\nexpansion: compute the matrix of minors with checkerboard signs, adjugate\n(transpose), and multiply by 1/determinant.\n\nReference: https://en.wikipedia.org/wiki/Invertible_matrix\n\nComplexity:\n    Time:  O(n! * n)  (cofactor expansion)\n    Space: O(n^2)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport fractions\n\n\ndef invert_matrix(\n    matrix: list[list[int | float]],\n) -> list[list[int | float | fractions.Fraction]]:\n    \"\"\"Invert an n x n matrix.\n\n    Args:\n        matrix: Square matrix of size n x n (n >= 2).\n\n    Returns:\n        Inverted matrix, or an error sentinel:\n        [[-1]] if not a valid matrix, [[-2]] if not square,\n        [[-3]] if too small, [[-4]] if singular.\n\n    Examples:\n        >>> invert_matrix([[1, 1], [1, 2]])\n        [[2, -1], [-1, 1]]\n    \"\"\"\n    if not _array_is_matrix(matrix):\n        return [[-1]]\n    elif len(matrix) != len(matrix[0]):\n        return [[-2]]\n    elif len(matrix) < 2:\n        return [[-3]]\n    elif get_determinant(matrix) == 0:\n        return [[-4]]\n    elif len(matrix) == 2:\n        multiplier = 1 / get_determinant(matrix)\n        inverted = [[multiplier] * len(matrix) for _ in range(len(matrix))]\n        inverted[0][1] = inverted[0][1] * -1 * matrix[0][1]\n        inverted[1][0] = inverted[1][0] * -1 * matrix[1][0]\n        inverted[0][0] = multiplier * matrix[1][1]\n        inverted[1][1] = multiplier * matrix[0][0]\n        return inverted\n    else:\n        matrix_of_minors = _get_matrix_of_minors(matrix)\n        multiplier = fractions.Fraction(1, get_determinant(matrix))\n        inverted = _transpose_and_multiply(matrix_of_minors, multiplier)\n        return inverted\n\n\ndef get_determinant(matrix: list[list[int | float]]) -> int | float:\n    \"\"\"Recursively compute the determinant of an n x n matrix.\n\n    Args:\n        matrix: Square matrix of size n x n (n >= 2).\n\n    Returns:\n        Determinant value.\n\n    Examples:\n        >>> get_determinant([[1, 2], [3, 4]])\n        -2\n    \"\"\"\n    if len(matrix) == 2:\n        return (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0])\n    sign = 1\n    det = 0\n    for i in range(len(matrix)):\n        det += sign * matrix[0][i] * get_determinant(_get_minor(matrix, 0, i))\n        sign *= -1\n    return det\n\n\ndef _get_matrix_of_minors(\n    matrix: list[list[int | float]],\n) -> list[list[int | float]]:\n    \"\"\"Compute the matrix of minors with alternating signs (cofactor matrix).\n\n    Args:\n        matrix: Square matrix of size n x n.\n\n    Returns:\n        Cofactor matrix.\n    \"\"\"\n    size = len(matrix)\n    result = [[0] * size for _ in range(size)]\n    for row in range(size):\n        for col in range(len(matrix[0])):\n            sign = 1 if (row + col) % 2 == 0 else -1\n            result[row][col] = sign * get_determinant(_get_minor(matrix, row, col))\n    return result\n\n\ndef _get_minor(\n    matrix: list[list[int | float]], row: int, col: int\n) -> list[list[int | float]]:\n    \"\"\"Extract the minor by removing the given row and column.\n\n    Args:\n        matrix: Square matrix.\n        row: Row index to remove.\n        col: Column index to remove.\n\n    Returns:\n        Sub-matrix with the specified row and column removed.\n    \"\"\"\n    minors = []\n    for i in range(len(matrix)):\n        if i != row:\n            new_row = matrix[i][:col]\n            new_row.extend(matrix[i][col + 1 :])\n            minors.append(new_row)\n    return minors\n\n\ndef _transpose_and_multiply(\n    matrix: list[list[int | float]],\n    multiplier: int | float | fractions.Fraction = 1,\n) -> list[list[int | float | fractions.Fraction]]:\n    \"\"\"Transpose the matrix and multiply each element by a scalar.\n\n    Args:\n        matrix: Square matrix to transpose.\n        multiplier: Scalar to multiply each element by.\n\n    Returns:\n        Transposed and scaled matrix.\n    \"\"\"\n    for row in range(len(matrix)):\n        for col in range(row + 1):\n            temp = matrix[row][col] * multiplier\n            matrix[row][col] = matrix[col][row] * multiplier\n            matrix[col][row] = temp\n    return matrix\n\n\ndef _array_is_matrix(matrix: list[list]) -> bool:\n    \"\"\"Check whether a 2D list has consistent row lengths.\n\n    Args:\n        matrix: 2D list to validate.\n\n    Returns:\n        True if all rows have the same length, False otherwise.\n    \"\"\"\n    if len(matrix) == 0:\n        return False\n    first_col = len(matrix[0])\n    return all(len(row) == first_col for row in matrix)\n"
  },
  {
    "path": "algorithms/matrix/multiply.py",
    "content": "\"\"\"\nMatrix Multiplication\n\nMultiply two compatible matrices and return their product. The number of\ncolumns in the multiplicand must equal the number of rows in the multiplier.\n\nReference: https://en.wikipedia.org/wiki/Matrix_multiplication\n\nComplexity:\n    Time:  O(m * n * p)  for (m x n) * (n x p)\n    Space: O(m * p)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef multiply(\n    multiplicand: list[list[int]], multiplier: list[list[int]]\n) -> list[list[int]]:\n    \"\"\"Multiply two matrices.\n\n    Args:\n        multiplicand: Matrix of size m x n.\n        multiplier: Matrix of size n x p.\n\n    Returns:\n        Product matrix of size m x p.\n\n    Raises:\n        Exception: If the matrices are not compatible for multiplication.\n\n    Examples:\n        >>> multiply([[1, 2, 3], [2, 1, 1]], [[1], [2], [3]])\n        [[14], [7]]\n    \"\"\"\n    multiplicand_rows, multiplicand_cols = len(multiplicand), len(multiplicand[0])\n    multiplier_rows, multiplier_cols = len(multiplier), len(multiplier[0])\n    if multiplicand_cols != multiplier_rows:\n        raise Exception(\"Multiplicand matrix not compatible with Multiplier matrix.\")\n    result = [[0] * multiplier_cols for _ in range(multiplicand_rows)]\n    for i in range(multiplicand_rows):\n        for j in range(multiplier_cols):\n            for k in range(len(multiplier)):\n                result[i][j] += multiplicand[i][k] * multiplier[k][j]\n    return result\n"
  },
  {
    "path": "algorithms/matrix/rotate_image.py",
    "content": "\"\"\"\nRotate Image\n\nRotate an n x n 2D matrix representing an image by 90 degrees clockwise,\nin-place. First reverse the rows top-to-bottom, then transpose.\n\nReference: https://leetcode.com/problems/rotate-image/\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef rotate(mat: list[list[int]]) -> list[list[int]]:\n    \"\"\"Rotate a square matrix 90 degrees clockwise in-place.\n\n    Args:\n        mat: Square matrix of size n x n.\n\n    Returns:\n        The same matrix, rotated 90 degrees clockwise.\n\n    Examples:\n        >>> rotate([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [[7, 4, 1], [8, 5, 2], [9, 6, 3]]\n    \"\"\"\n    if not mat:\n        return mat\n    mat.reverse()\n    for i in range(len(mat)):\n        for j in range(i):\n            mat[i][j], mat[j][i] = mat[j][i], mat[i][j]\n    return mat\n"
  },
  {
    "path": "algorithms/matrix/search_in_sorted_matrix.py",
    "content": "\"\"\"\nSearch in Sorted Matrix\n\nSearch for a key in a matrix that is sorted row-wise and column-wise\nin non-decreasing order. Start from the bottom-left corner and move\nup or right depending on the comparison.\n\nReference: https://leetcode.com/problems/search-a-2d-matrix-ii/\n\nComplexity:\n    Time:  O(m + n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef search_in_a_sorted_matrix(\n    mat: list[list[int]], rows: int, cols: int, key: int\n) -> bool:\n    \"\"\"Search for a key in a row-wise and column-wise sorted matrix.\n\n    Args:\n        mat: Sorted matrix of size m x n.\n        rows: Number of rows.\n        cols: Number of columns.\n        key: Value to search for.\n\n    Returns:\n        True if the key is found, False otherwise.\n\n    Examples:\n        >>> search_in_a_sorted_matrix([[2, 5, 7], [4, 8, 13], [9, 11, 15]], 3, 3, 13)\n        True\n        >>> search_in_a_sorted_matrix([[2, 5, 7], [4, 8, 13], [9, 11, 15]], 3, 3, 6)\n        False\n    \"\"\"\n    i, j = rows - 1, 0\n    while i >= 0 and j < cols:\n        if key == mat[i][j]:\n            return True\n        if key < mat[i][j]:\n            i -= 1\n        else:\n            j += 1\n    return False\n"
  },
  {
    "path": "algorithms/matrix/sort_matrix_diagonally.py",
    "content": "\"\"\"\nSort Matrix Diagonally\n\nGiven an m x n matrix of integers, sort each diagonal from top-left to\nbottom-right in ascending order and return the sorted matrix. Uses a\nmin-heap for each diagonal.\n\nReference: https://leetcode.com/problems/sort-the-matrix-diagonally/\n\nComplexity:\n    Time:  O((m + n) * k * log k)  where k = min(m, n)\n    Space: O(min(m, n))\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom heapq import heappop, heappush\n\n\ndef sort_diagonally(mat: list[list[int]]) -> list[list[int]]:\n    \"\"\"Sort each top-left to bottom-right diagonal in ascending order.\n\n    Args:\n        mat: Matrix of size m x n.\n\n    Returns:\n        The matrix with each diagonal sorted.\n\n    Examples:\n        >>> sort_diagonally([[3, 3, 1, 1], [2, 2, 1, 2], [1, 1, 1, 2]])\n        [[1, 1, 1, 1], [1, 2, 2, 2], [1, 2, 3, 3]]\n    \"\"\"\n    if len(mat) == 1 or len(mat[0]) == 1:\n        return mat\n\n    num_rows = len(mat)\n    num_cols = len(mat[0])\n\n    for i in range(num_rows + num_cols - 1):\n        if i + 1 < num_rows:\n            heap: list[int] = []\n            row = num_rows - (i + 1)\n            col = 0\n            while row < num_rows:\n                heappush(heap, mat[row][col])\n                row += 1\n                col += 1\n            row = num_rows - (i + 1)\n            col = 0\n            while heap:\n                mat[row][col] = heappop(heap)\n                row += 1\n                col += 1\n        else:\n            heap = []\n            row = 0\n            col = i - (num_rows - 1)\n            while col < num_cols and row < num_rows:\n                heappush(heap, mat[row][col])\n                row += 1\n                col += 1\n            row = 0\n            col = i - (num_rows - 1)\n            while heap:\n                mat[row][col] = heappop(heap)\n                row += 1\n                col += 1\n\n    return mat\n"
  },
  {
    "path": "algorithms/matrix/sparse_dot_vector.py",
    "content": "\"\"\"\nSparse Dot Vector\n\nCompute the dot product of two large sparse vectors efficiently by\nconverting them to index-value pair representations and merging.\n\nReference: https://leetcode.com/problems/dot-product-of-two-sparse-vectors/\n\nComplexity:\n    Time:  O(n) for conversion, O(k) for dot product where k = non-zero count\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef vector_to_index_value_list(\n    vector: list[float],\n) -> list[tuple[int, float]]:\n    \"\"\"Convert a dense vector to a sparse index-value list.\n\n    Args:\n        vector: Dense vector of floats.\n\n    Returns:\n        List of (index, value) tuples for non-zero elements.\n\n    Examples:\n        >>> vector_to_index_value_list([0.0, 2.0, 0.0, 3.0])\n        [(1, 2.0), (3, 3.0)]\n    \"\"\"\n    return [(i, v) for i, v in enumerate(vector) if v != 0.0]\n\n\ndef dot_product(\n    iv_list1: list[tuple[int, float]],\n    iv_list2: list[tuple[int, float]],\n) -> float:\n    \"\"\"Compute the dot product of two sparse index-value lists.\n\n    Args:\n        iv_list1: First sparse vector as index-value pairs.\n        iv_list2: Second sparse vector as index-value pairs.\n\n    Returns:\n        Dot product of the two vectors.\n\n    Examples:\n        >>> dot_product([(0, 1.0), (1, 2.0), (2, 3.0)], [(1, 2.0), (2, 2.0)])\n        10.0\n    \"\"\"\n    product = 0\n    p1 = len(iv_list1) - 1\n    p2 = len(iv_list2) - 1\n\n    while p1 >= 0 and p2 >= 0:\n        i1, v1 = iv_list1[p1]\n        i2, v2 = iv_list2[p2]\n\n        if i1 < i2:\n            p1 -= 1\n        elif i2 < i1:\n            p2 -= 1\n        else:\n            product += v1 * v2\n            p1 -= 1\n            p2 -= 1\n\n    return product\n"
  },
  {
    "path": "algorithms/matrix/sparse_mul.py",
    "content": "\"\"\"\nSparse Matrix Multiplication\n\nGiven two sparse matrices A and B, return their product A * B.\nSkips zero elements for efficiency. A's column count must equal\nB's row count.\n\nReference: https://leetcode.com/problems/sparse-matrix-multiplication/\n\nComplexity:\n    Time:  O(m * n * p) worst case, better with sparsity\n    Space: O(m * p)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef sparse_multiply(\n    mat_a: list[list[int]], mat_b: list[list[int]]\n) -> list[list[int]] | None:\n    \"\"\"Multiply two sparse matrices, skipping zero elements.\n\n    Args:\n        mat_a: First matrix of size m x n.\n        mat_b: Second matrix of size n x p.\n\n    Returns:\n        Product matrix of size m x p, or None if either input is None.\n\n    Raises:\n        Exception: If the matrices have incompatible dimensions.\n\n    Examples:\n        >>> sparse_multiply([[1, 0, 0], [-1, 0, 3]], [[7, 0, 0], [0, 0, 0], [0, 0, 1]])\n        [[7, 0, 0], [-7, 0, 3]]\n    \"\"\"\n    if mat_a is None or mat_b is None:\n        return None\n    rows_a, cols_a = len(mat_a), len(mat_a[0])\n    cols_b = len(mat_b[0])\n    if len(mat_b) != cols_a:\n        raise Exception(\"A's column number must be equal to B's row number.\")\n    result = [[0] * cols_b for _ in range(rows_a)]\n    for i, row in enumerate(mat_a):\n        for k, elem_a in enumerate(row):\n            if elem_a:\n                for j, elem_b in enumerate(mat_b[k]):\n                    if elem_b:\n                        result[i][j] += elem_a * elem_b\n    return result\n"
  },
  {
    "path": "algorithms/matrix/spiral_traversal.py",
    "content": "\"\"\"\nSpiral Traversal\n\nReturn all elements of an m x n matrix in spiral order, traversing\nright, down, left, and up repeatedly while shrinking the boundaries.\n\nReference: https://leetcode.com/problems/spiral-matrix/\n\nComplexity:\n    Time:  O(m * n)\n    Space: O(m * n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef spiral_traversal(matrix: list[list[int]]) -> list[int]:\n    \"\"\"Return matrix elements in spiral order.\n\n    Args:\n        matrix: 2D list of integers (m x n).\n\n    Returns:\n        List of elements in spiral order.\n\n    Examples:\n        >>> spiral_traversal([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n        [1, 2, 3, 6, 9, 8, 7, 4, 5]\n    \"\"\"\n    result: list[int] = []\n    if len(matrix) == 0:\n        return result\n    row_begin = 0\n    row_end = len(matrix) - 1\n    col_begin = 0\n    col_end = len(matrix[0]) - 1\n\n    while row_begin <= row_end and col_begin <= col_end:\n        for i in range(col_begin, col_end + 1):\n            result.append(matrix[row_begin][i])\n        row_begin += 1\n\n        for i in range(row_begin, row_end + 1):\n            result.append(matrix[i][col_end])\n        col_end -= 1\n\n        if row_begin <= row_end:\n            for i in range(col_end, col_begin - 1, -1):\n                result.append(matrix[row_end][i])\n        row_end -= 1\n\n        if col_begin <= col_end:\n            for i in range(row_end, row_begin - 1, -1):\n                result.append(matrix[i][col_begin])\n        col_begin += 1\n\n    return result\n"
  },
  {
    "path": "algorithms/matrix/sudoku_validator.py",
    "content": "\"\"\"\nSudoku Validator\n\nValidate whether a completed 9x9 Sudoku board is a valid solution.\nEach row, column, and 3x3 sub-box must contain digits 1-9 without\nrepetition. Boards containing zeroes are considered invalid.\n\nReference: https://en.wikipedia.org/wiki/Sudoku\n\nComplexity:\n    Time:  O(1)  (fixed 9x9 board)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import defaultdict\n\n\ndef valid_solution_hashtable(board: list[list[int]]) -> bool:\n    \"\"\"Validate a Sudoku solution using hash tables.\n\n    Args:\n        board: 9x9 grid of integers (1-9 for valid, 0 for empty).\n\n    Returns:\n        True if the board is a valid Sudoku solution.\n\n    Examples:\n        >>> board = [[5,3,4,6,7,8,9,1,2],[6,7,2,1,9,5,3,4,8],\n        ...          [1,9,8,3,4,2,5,6,7],[8,5,9,7,6,1,4,2,3],\n        ...          [4,2,6,8,5,3,7,9,1],[7,1,3,9,2,4,8,5,6],\n        ...          [9,6,1,5,3,7,2,8,4],[2,8,7,4,1,9,6,3,5],\n        ...          [3,4,5,2,8,6,1,7,9]]\n        >>> valid_solution_hashtable(board)\n        True\n    \"\"\"\n    for i in range(len(board)):\n        row_seen = defaultdict(int)\n        col_seen = defaultdict(int)\n        for j in range(len(board[0])):\n            value_row = board[i][j]\n            value_col = board[j][i]\n            if not value_row or value_col == 0:\n                return False\n            if value_row in row_seen:\n                return False\n            else:\n                row_seen[value_row] += 1\n            if value_col in col_seen:\n                return False\n            else:\n                col_seen[value_col] += 1\n\n    for i in range(3):\n        for j in range(3):\n            grid_sum = 0\n            for k in range(3):\n                for m in range(3):\n                    grid_sum += board[i * 3 + k][j * 3 + m]\n            if grid_sum != 45:\n                return False\n    return True\n\n\ndef valid_solution(board: list[list[int]]) -> bool:\n    \"\"\"Validate a Sudoku solution using sorted comparison.\n\n    Args:\n        board: 9x9 grid of integers (1-9 for valid, 0 for empty).\n\n    Returns:\n        True if the board is a valid Sudoku solution.\n\n    Examples:\n        >>> board = [[5,3,4,6,7,8,9,1,2],[6,7,2,1,9,5,3,4,8],\n        ...          [1,9,8,3,4,2,5,6,7],[8,5,9,7,6,1,4,2,3],\n        ...          [4,2,6,8,5,3,7,9,1],[7,1,3,9,2,4,8,5,6],\n        ...          [9,6,1,5,3,7,2,8,4],[2,8,7,4,1,9,6,3,5],\n        ...          [3,4,5,2,8,6,1,7,9]]\n        >>> valid_solution(board)\n        True\n    \"\"\"\n    correct = [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n    for row in board:\n        if sorted(row) != correct:\n            return False\n\n    for column in zip(*board, strict=False):\n        if sorted(column) != correct:\n            return False\n\n    for i in range(3):\n        for j in range(3):\n            region = []\n            for line in board[i * 3 : (i + 1) * 3]:\n                region += line[j * 3 : (j + 1) * 3]\n            if sorted(region) != correct:\n                return False\n\n    return True\n\n\ndef valid_solution_set(board: list[list[int]]) -> bool:\n    \"\"\"Validate a Sudoku solution using set comparison.\n\n    Args:\n        board: 9x9 grid of integers (1-9 for valid, 0 for empty).\n\n    Returns:\n        True if the board is a valid Sudoku solution.\n\n    Examples:\n        >>> board = [[5,3,4,6,7,8,9,1,2],[6,7,2,1,9,5,3,4,8],\n        ...          [1,9,8,3,4,2,5,6,7],[8,5,9,7,6,1,4,2,3],\n        ...          [4,2,6,8,5,3,7,9,1],[7,1,3,9,2,4,8,5,6],\n        ...          [9,6,1,5,3,7,2,8,4],[2,8,7,4,1,9,6,3,5],\n        ...          [3,4,5,2,8,6,1,7,9]]\n        >>> valid_solution_set(board)\n        True\n    \"\"\"\n    valid = set(range(1, 10))\n\n    for row in board:\n        if set(row) != valid:\n            return False\n\n    for col in [[row[i] for row in board] for i in range(9)]:\n        if set(col) != valid:\n            return False\n\n    for x in range(3):\n        for y in range(3):\n            if (\n                set(\n                    sum(\n                        [\n                            row[x * 3 : (x + 1) * 3]\n                            for row in board[y * 3 : (y + 1) * 3]\n                        ],\n                        [],\n                    )\n                )\n                != valid\n            ):\n                return False\n\n    return True\n"
  },
  {
    "path": "algorithms/matrix/sum_sub_squares.py",
    "content": "\"\"\"\nSum of Sub-Squares\n\nGiven a square matrix of size n x n and an integer k, compute the sum\nof all k x k sub-squares and return the results as a matrix.\n\nReference: https://www.geeksforgeeks.org/given-n-x-n-square-matrix-find-sum-sub-squares-size-k-x-k/\n\nComplexity:\n    Time:  O(n^2 * k^2)\n    Space: O((n - k + 1)^2)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef sum_sub_squares(matrix: list[list[int]], k: int) -> list[list[int]] | None:\n    \"\"\"Compute sums of all k x k sub-squares in the matrix.\n\n    Args:\n        matrix: Square matrix of size n x n.\n        k: Side length of the sub-squares.\n\n    Returns:\n        Matrix of sub-square sums, or None if k > n.\n\n    Examples:\n        >>> sum_sub_squares([[1, 1, 1], [2, 2, 2], [3, 3, 3]], 2)\n        [[6, 6], [9, 9]]\n    \"\"\"\n    size = len(matrix)\n    if k > size:\n        return None\n    result_size = size - k + 1\n    result = [[0] * result_size for _ in range(result_size)]\n    for i in range(result_size):\n        for j in range(result_size):\n            total = 0\n            for p in range(i, k + i):\n                for q in range(j, k + j):\n                    total += matrix[p][q]\n            result[i][j] = total\n    return result\n"
  },
  {
    "path": "algorithms/py.typed",
    "content": ""
  },
  {
    "path": "algorithms/queue/__init__.py",
    "content": "\"\"\"Queue-based algorithm implementations.\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.priority_queue import PriorityQueue, PriorityQueueNode\nfrom algorithms.data_structures.queue import (\n    AbstractQueue,\n    ArrayQueue,\n    LinkedListQueue,\n    QueueNode,\n)\n\nfrom .max_sliding_window import max_sliding_window\nfrom .moving_average import MovingAverage\nfrom .reconstruct_queue import reconstruct_queue\nfrom .zigzagiterator import ZigZagIterator\n\n__all__ = [\n    \"AbstractQueue\",\n    \"ArrayQueue\",\n    \"LinkedListQueue\",\n    \"MovingAverage\",\n    \"PriorityQueue\",\n    \"PriorityQueueNode\",\n    \"QueueNode\",\n    \"ZigZagIterator\",\n    \"max_sliding_window\",\n    \"reconstruct_queue\",\n]\n"
  },
  {
    "path": "algorithms/queue/max_sliding_window.py",
    "content": "\"\"\"\nMax Sliding Window (Deque-based)\n\nGiven an array and a window size k, find the maximum element in each\nsliding window using a monotonic deque that stores indices of elements\nin decreasing order of their values.\n\nReference: https://leetcode.com/problems/sliding-window-maximum/\n\nComplexity:\n    Time:  O(n)\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef max_sliding_window(arr: list[int], k: int) -> list[int]:\n    \"\"\"Find the maximum in each sliding window of size k.\n\n    Uses a deque to maintain indices of useful elements in decreasing\n    order. The front of the deque always holds the index of the current\n    window maximum.\n\n    Args:\n        arr: Input array of integers.\n        k: Window size.\n\n    Returns:\n        List of maximum values for each window position.\n\n    Examples:\n        >>> max_sliding_window([1, 3, -1, -3, 5, 3, 6, 7], 3)\n        [3, 3, 5, 5, 6, 7]\n    \"\"\"\n    index_deque: collections.deque[int] = collections.deque()\n    result: list[int] = []\n    for i, value in enumerate(arr):\n        while index_deque and arr[index_deque[-1]] < value:\n            index_deque.pop()\n        index_deque.append(i)\n        if index_deque[0] == i - k:\n            index_deque.popleft()\n        if i >= k - 1:\n            result.append(arr[index_deque[0]])\n    return result\n"
  },
  {
    "path": "algorithms/queue/moving_average.py",
    "content": "\"\"\"\nMoving Average from Data Stream\n\nCalculate the moving average of integers in a sliding window of fixed\nsize using a bounded deque.\n\nReference: https://leetcode.com/problems/moving-average-from-data-stream/\n\nComplexity:\n    Time:  O(1) per call to next\n    Space: O(size)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\nclass MovingAverage:\n    \"\"\"Computes the moving average over a sliding window.\n\n    Examples:\n        >>> m = MovingAverage(3)\n        >>> m.next(1)\n        1.0\n        >>> m.next(10)\n        5.5\n    \"\"\"\n\n    def __init__(self, size: int) -> None:\n        \"\"\"Initialize the moving average calculator.\n\n        Args:\n            size: The window size for the moving average.\n        \"\"\"\n        self.queue: deque[int] = deque(maxlen=size)\n\n    def next(self, val: int) -> float:\n        \"\"\"Add a value and return the current moving average.\n\n        Args:\n            val: The next integer in the data stream.\n\n        Returns:\n            The current moving average as a float.\n        \"\"\"\n        self.queue.append(val)\n        return sum(self.queue) / len(self.queue)\n"
  },
  {
    "path": "algorithms/queue/reconstruct_queue.py",
    "content": "\"\"\"\nReconstruct Queue by Height\n\nGiven a list of people described by (height, k) pairs where k is the\nnumber of taller-or-equal people in front, reconstruct the queue by\nsorting and inserting.\n\nReference: https://leetcode.com/problems/queue-reconstruction-by-height/\n\nComplexity:\n    Time:  O(n^2)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef reconstruct_queue(people: list[list[int]]) -> list[list[int]]:\n    \"\"\"Reconstruct the queue from (height, k) pairs.\n\n    Args:\n        people: List of [height, k] pairs.\n\n    Returns:\n        The reconstructed queue as a list of [height, k] pairs.\n\n    Examples:\n        >>> reconstruct_queue([[7, 0], [4, 4], [7, 1], [5, 0], [6, 1], [5, 2]])\n        [[5, 0], [7, 0], [5, 2], [6, 1], [4, 4], [7, 1]]\n    \"\"\"\n    queue: list[list[int]] = []\n    people.sort(key=lambda x: (-x[0], x[1]))\n    for height, count in people:\n        queue.insert(count, [height, count])\n    return queue\n"
  },
  {
    "path": "algorithms/queue/zigzagiterator.py",
    "content": "\"\"\"\nZigzag Iterator\n\nInterleave elements from two lists in a zigzag fashion. Elements are\nyielded alternately from each list until both are exhausted.\n\nReference: https://leetcode.com/problems/zigzag-iterator/\n\nComplexity:\n    Time:  O(n) total across all next() calls\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\n\nclass ZigZagIterator:\n    \"\"\"Iterator that interleaves elements from two lists.\n\n    Examples:\n        >>> it = ZigZagIterator([1, 2], [3, 4, 5])\n        >>> it.next()\n        1\n        >>> it.next()\n        3\n    \"\"\"\n\n    def __init__(self, v1: list[int], v2: list[int]) -> None:\n        \"\"\"Initialize with two lists.\n\n        Args:\n            v1: First input list.\n            v2: Second input list.\n        \"\"\"\n        self.queue: deque[list[int]] = deque(lst for lst in (v1, v2) if lst)\n\n    def next(self) -> int:\n        \"\"\"Return the next element in zigzag order.\n\n        Returns:\n            The next interleaved element.\n        \"\"\"\n        current_list = self.queue.popleft()\n        ret = current_list.pop(0)\n        if current_list:\n            self.queue.append(current_list)\n        return ret\n\n    def has_next(self) -> bool:\n        \"\"\"Check if there are more elements.\n\n        Returns:\n            True if elements remain, False otherwise.\n        \"\"\"\n        return bool(self.queue)\n"
  },
  {
    "path": "algorithms/searching/__init__.py",
    "content": "\"\"\"Collection of search algorithms: finding the needle in a haystack.\"\"\"\n\nfrom algorithms.searching.binary_search import binary_search, binary_search_recur\nfrom algorithms.searching.exponential_search import exponential_search\nfrom algorithms.searching.find_min_rotate import find_min_rotate, find_min_rotate_recur\nfrom algorithms.searching.first_occurrence import first_occurrence\nfrom algorithms.searching.generalized_binary_search import binary_search_first_true\nfrom algorithms.searching.interpolation_search import interpolation_search\nfrom algorithms.searching.jump_search import jump_search\nfrom algorithms.searching.last_occurrence import last_occurrence\nfrom algorithms.searching.linear_search import linear_search\nfrom algorithms.searching.next_greatest_letter import (\n    next_greatest_letter,\n    next_greatest_letter_v1,\n    next_greatest_letter_v2,\n)\nfrom algorithms.searching.search_insert import search_insert\nfrom algorithms.searching.search_range import search_range\nfrom algorithms.searching.search_rotate import search_rotate, search_rotate_recur\nfrom algorithms.searching.sentinel_search import sentinel_search\nfrom algorithms.searching.ternary_search import ternary_search\nfrom algorithms.searching.two_sum import two_sum, two_sum1, two_sum2\n\n__all__ = [\n    \"binary_search\",\n    \"binary_search_first_true\",\n    \"binary_search_recur\",\n    \"find_min_rotate\",\n    \"find_min_rotate_recur\",\n    \"first_occurrence\",\n    \"interpolation_search\",\n    \"jump_search\",\n    \"last_occurrence\",\n    \"linear_search\",\n    \"next_greatest_letter\",\n    \"next_greatest_letter_v1\",\n    \"next_greatest_letter_v2\",\n    \"search_insert\",\n    \"search_range\",\n    \"search_rotate\",\n    \"search_rotate_recur\",\n    \"ternary_search\",\n    \"two_sum\",\n    \"two_sum1\",\n    \"two_sum2\",\n    \"exponential_search\",\n    \"sentinel_search\",\n]\n"
  },
  {
    "path": "algorithms/searching/binary_search.py",
    "content": "\"\"\"\nBinary Search\n\nSearch for an element in a sorted array by repeatedly dividing the search\ninterval in half.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1) iterative, O(log n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef binary_search(array: list[int], query: int) -> int:\n    \"\"\"Search for *query* in a sorted *array* using iterative binary search.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        query: Value to search for.\n\n    Returns:\n        Index of *query* in *array*, or -1 if not found.\n\n    Examples:\n        >>> binary_search([1, 2, 3, 4, 5], 3)\n        2\n        >>> binary_search([1, 2, 3, 4, 5], 6)\n        -1\n    \"\"\"\n    low, high = 0, len(array) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        val = array[mid]\n        if val == query:\n            return mid\n        if val < query:\n            low = mid + 1\n        else:\n            high = mid - 1\n    return -1\n\n\ndef binary_search_recur(array: list[int], low: int, high: int, val: int) -> int:\n    \"\"\"Search for *val* in a sorted *array* using recursive binary search.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        low: Lower bound index of the current search range.\n        high: Upper bound index of the current search range.\n        val: Value to search for.\n\n    Returns:\n        Index of *val* in *array*, or -1 if not found.\n\n    Examples:\n        >>> binary_search_recur([1, 2, 3, 4, 5], 0, 4, 3)\n        2\n        >>> binary_search_recur([1, 2, 3, 4, 5], 0, 4, 6)\n        -1\n    \"\"\"\n    if low > high:\n        return -1\n    mid = low + (high - low) // 2\n    if val < array[mid]:\n        return binary_search_recur(array, low, mid - 1, val)\n    if val > array[mid]:\n        return binary_search_recur(array, mid + 1, high, val)\n    return mid\n"
  },
  {
    "path": "algorithms/searching/exponential_search.py",
    "content": "\"\"\"Exponential search — locate an element in a sorted array.\n\nFirst finds a range where the target may lie by doubling the index,\nthen performs binary search within that range. Useful when the target\nis near the beginning of a large or unbounded list.\n\nTime: O(log i) where i is the index of the target.\n\nInspired by PR #867 (yuviii99).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef exponential_search(arr: list[int], target: int) -> int:\n    \"\"\"Return the index of *target* in sorted *arr*, or -1 if absent.\"\"\"\n    if not arr:\n        return -1\n    if arr[0] == target:\n        return 0\n    bound = 1\n    while bound < len(arr) and arr[bound] <= target:\n        bound *= 2\n    low = bound // 2\n    high = min(bound, len(arr) - 1)\n    while low <= high:\n        mid = (low + high) // 2\n        if arr[mid] == target:\n            return mid\n        elif arr[mid] < target:\n            low = mid + 1\n        else:\n            high = mid - 1\n    return -1\n"
  },
  {
    "path": "algorithms/searching/find_min_rotate.py",
    "content": "\"\"\"\nFind Minimum in Rotated Sorted Array\n\nFind the minimum element in a sorted array that has been rotated at some\nunknown pivot. Assumes no duplicates exist in the array.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1) iterative, O(log n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef find_min_rotate(array: list[int]) -> int:\n    \"\"\"Find the minimum element in a rotated sorted array (iterative).\n\n    Args:\n        array: A sorted list of unique integers that has been rotated.\n\n    Returns:\n        The minimum value in the array.\n\n    Examples:\n        >>> find_min_rotate([4, 5, 6, 7, 0, 1, 2])\n        0\n        >>> find_min_rotate([1, 2, 3])\n        1\n    \"\"\"\n    low = 0\n    high = len(array) - 1\n    while low < high:\n        mid = (low + high) // 2\n        if array[mid] > array[high]:\n            low = mid + 1\n        else:\n            high = mid\n    return array[low]\n\n\ndef find_min_rotate_recur(array: list[int], low: int, high: int) -> int:\n    \"\"\"Find the minimum element in a rotated sorted array (recursive).\n\n    Args:\n        array: A sorted list of unique integers that has been rotated.\n        low: Lower bound index of the current search range.\n        high: Upper bound index of the current search range.\n\n    Returns:\n        The minimum value in the array.\n\n    Examples:\n        >>> find_min_rotate_recur([4, 5, 6, 7, 0, 1, 2], 0, 6)\n        0\n    \"\"\"\n    mid = (low + high) // 2\n    if mid == low:\n        return array[low]\n    if array[mid] > array[high]:\n        return find_min_rotate_recur(array, mid + 1, high)\n    return find_min_rotate_recur(array, low, mid)\n"
  },
  {
    "path": "algorithms/searching/first_occurrence.py",
    "content": "\"\"\"\nFirst Occurrence\n\nFind the index of the first occurrence of a target value in a sorted array\nusing binary search.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef first_occurrence(array: list[int], query: int) -> int:\n    \"\"\"Find the index of the first occurrence of *query* in *array*.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        query: Value to search for.\n\n    Returns:\n        Index of the first occurrence of *query*, or -1 if not found.\n\n    Examples:\n        >>> first_occurrence([1, 2, 2, 2, 3, 4], 2)\n        1\n        >>> first_occurrence([1, 2, 3, 4, 5], 6)\n        -1\n    \"\"\"\n    low, high = 0, len(array) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if low == high:\n            break\n        if array[mid] < query:\n            low = mid + 1\n        else:\n            high = mid\n    if array[low] == query:\n        return low\n    return -1\n"
  },
  {
    "path": "algorithms/searching/generalized_binary_search.py",
    "content": "\"\"\"\nGeneralized Binary Search\n\nFind the smallest value in a numeric range for which a monotonic boolean\npredicate evaluates to True.  Instead of searching for a specific value in\nan array, this version accepts an arbitrary predicate, allowing the same\nbinary search logic to be reused across many problem domains.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(log n)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Callable\n\n\ndef binary_search_first_true(\n    low: int,\n    high: int,\n    predicate: Callable[[int], bool],\n) -> int:\n    \"\"\"Find the smallest *x* in [low, high] where *predicate(x)* is True.\n\n    The predicate must be monotonic: once it returns True for some value,\n    it must return True for all larger values in the range.\n\n    Args:\n        low: Lower bound of the search range (inclusive).\n        high: Upper bound of the search range (inclusive).\n        predicate: A monotonic boolean function.\n\n    Returns:\n        The smallest *x* for which *predicate(x)* is True, or -1 if no\n        such value exists in the range.\n\n    Examples:\n        >>> binary_search_first_true(0, 10, lambda x: x >= 7)\n        7\n        >>> binary_search_first_true(0, 10, lambda x: x * x >= 25)\n        5\n        >>> binary_search_first_true(0, 5, lambda x: x > 10)\n        -1\n    \"\"\"\n    result = -1\n\n    while low <= high:\n        mid = low + (high - low) // 2\n\n        if predicate(mid):\n            result = mid\n            high = mid - 1\n        else:\n            low = mid + 1\n\n    return result\n\n\nif __name__ == \"__main__\":\n    print(binary_search_first_true(0, 10, lambda x: x >= 7))  # 7\n    print(binary_search_first_true(0, 10, lambda x: x * x >= 25))  # 5\n    print(binary_search_first_true(0, 5, lambda x: x > 10))  # -1\n"
  },
  {
    "path": "algorithms/searching/interpolation_search.py",
    "content": "\"\"\"\nInterpolation Search\n\nSearch for a target value in a uniformly distributed sorted array by\nestimating the position of the target using linear interpolation.\n\nReference: https://en.wikipedia.org/wiki/Interpolation_search\n\nComplexity:\n    Time:  O(1) best / O(log log n) average / O(n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef interpolation_search(array: list[int], search_key: int) -> int:\n    \"\"\"Search for *search_key* in a sorted *array* using interpolation search.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        search_key: Value to search for.\n\n    Returns:\n        Index of *search_key* in *array*, or -1 if not found.\n\n    Examples:\n        >>> interpolation_search([-25, -12, -1, 10, 12, 15, 20, 41, 55], -1)\n        2\n        >>> interpolation_search([5, 10, 12, 14, 17, 20, 21], 55)\n        -1\n        >>> interpolation_search([5, 10, 12, 14, 17, 20, 21], -5)\n        -1\n    \"\"\"\n    high = len(array) - 1\n    low = 0\n\n    while (low <= high) and (array[low] <= search_key <= array[high]):\n        pos = low + int(\n            ((search_key - array[low]) * (high - low)) / (array[high] - array[low])\n        )\n\n        if array[pos] == search_key:\n            return pos\n\n        if array[pos] < search_key:\n            low = pos + 1\n        else:\n            high = pos - 1\n\n    return -1\n\n\nif __name__ == \"__main__\":\n    import doctest\n\n    doctest.testmod()\n"
  },
  {
    "path": "algorithms/searching/jump_search.py",
    "content": "\"\"\"\nJump Search\n\nSearch for a target value in a sorted array by jumping ahead in fixed-size\nblocks and then performing a linear search within the identified block.\n\nReference: https://en.wikipedia.org/wiki/Jump_search\n\nComplexity:\n    Time:  O(1) best / O(sqrt n) average / O(sqrt n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\n\n\ndef jump_search(array: list[int], target: int) -> int:\n    \"\"\"Search for *target* in a sorted *array* using jump search.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        target: Value to search for.\n\n    Returns:\n        Index of *target* in *array*, or -1 if not found.\n\n    Examples:\n        >>> jump_search([1, 2, 3, 4, 5, 6, 7, 8, 9], 5)\n        4\n        >>> jump_search([1, 2, 3, 4, 5], 0)\n        -1\n    \"\"\"\n    length = len(array)\n    block_size = int(math.sqrt(length))\n    block_prev = 0\n    block = block_size\n\n    if array[length - 1] < target:\n        return -1\n    while block <= length and array[block - 1] < target:\n        block_prev = block\n        block += block_size\n\n    while array[block_prev] < target:\n        block_prev += 1\n        if block_prev == min(block, length):\n            return -1\n\n    if array[block_prev] == target:\n        return block_prev\n    return -1\n"
  },
  {
    "path": "algorithms/searching/last_occurrence.py",
    "content": "\"\"\"\nLast Occurrence\n\nFind the index of the last occurrence of a target value in a sorted array\nusing binary search.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef last_occurrence(array: list[int], query: int) -> int:\n    \"\"\"Find the index of the last occurrence of *query* in *array*.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        query: Value to search for.\n\n    Returns:\n        Index of the last occurrence of *query*, or -1 if not found.\n\n    Examples:\n        >>> last_occurrence([1, 2, 2, 2, 3, 4], 2)\n        3\n        >>> last_occurrence([1, 2, 3, 4, 5], 6)\n        -1\n    \"\"\"\n    low, high = 0, len(array) - 1\n    while low <= high:\n        mid = (high + low) // 2\n        if (array[mid] == query and mid == len(array) - 1) or (\n            array[mid] == query and array[mid + 1] > query\n        ):\n            return mid\n        if array[mid] <= query:\n            low = mid + 1\n        else:\n            high = mid - 1\n    return -1\n"
  },
  {
    "path": "algorithms/searching/linear_search.py",
    "content": "\"\"\"\nLinear Search\n\nSearch for a target value in an array by checking every element sequentially.\nThe array does not need to be sorted.\n\nReference: https://en.wikipedia.org/wiki/Linear_search\n\nComplexity:\n    Time:  O(1) best / O(n) average / O(n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef linear_search(array: list[int], query: int) -> int:\n    \"\"\"Search for *query* in *array* using linear search.\n\n    Args:\n        array: List of integers (order does not matter).\n        query: Value to search for.\n\n    Returns:\n        Index of *query* in *array*, or -1 if not found.\n\n    Examples:\n        >>> linear_search([5, 1, 3, 2, 4], 3)\n        2\n        >>> linear_search([5, 1, 3, 2, 4], 6)\n        -1\n    \"\"\"\n    for i, value in enumerate(array):\n        if value == query:\n            return i\n    return -1\n"
  },
  {
    "path": "algorithms/searching/next_greatest_letter.py",
    "content": "\"\"\"\nNext Greatest Letter\n\nGiven a sorted list of lowercase letters and a target letter, find the\nsmallest letter in the list that is larger than the target. Letters wrap\naround, so if the target is greater than or equal to the last letter the\nanswer is the first letter.\n\nReference: https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/\n\nComplexity:\n    next_greatest_letter        -- O(log n) time, O(1) space  (bisect)\n    next_greatest_letter_v1     -- O(log n) time, O(1) space  (manual binary search)\n    next_greatest_letter_v2     -- O(n) time, O(1) space      (brute force)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport bisect\n\n\ndef next_greatest_letter(letters: list[str], target: str) -> str:\n    \"\"\"Find the smallest letter greater than *target* using ``bisect``.\n\n    Args:\n        letters: Sorted list of lowercase letters.\n        target: The letter to exceed.\n\n    Returns:\n        The smallest letter in *letters* that is strictly greater than\n        *target*, wrapping around if necessary.\n\n    Examples:\n        >>> next_greatest_letter([\"c\", \"f\", \"j\"], \"a\")\n        'c'\n        >>> next_greatest_letter([\"c\", \"f\", \"j\"], \"c\")\n        'f'\n    \"\"\"\n    index = bisect.bisect(letters, target)\n    return letters[index % len(letters)]\n\n\ndef next_greatest_letter_v1(letters: list[str], target: str) -> str:\n    \"\"\"Find the smallest letter greater than *target* using binary search.\n\n    Args:\n        letters: Sorted list of lowercase letters.\n        target: The letter to exceed.\n\n    Returns:\n        The smallest letter in *letters* that is strictly greater than\n        *target*, wrapping around if necessary.\n\n    Examples:\n        >>> next_greatest_letter_v1([\"c\", \"f\", \"j\"], \"d\")\n        'f'\n    \"\"\"\n    if letters[0] > target:\n        return letters[0]\n    if letters[len(letters) - 1] <= target:\n        return letters[0]\n    left, right = 0, len(letters) - 1\n    while left <= right:\n        mid = left + (right - left) // 2\n        if letters[mid] > target:\n            right = mid - 1\n        else:\n            left = mid + 1\n    return letters[left]\n\n\ndef next_greatest_letter_v2(letters: list[str], target: str) -> str:\n    \"\"\"Find the smallest letter greater than *target* using brute force.\n\n    Args:\n        letters: Sorted list of lowercase letters.\n        target: The letter to exceed.\n\n    Returns:\n        The smallest letter in *letters* that is strictly greater than\n        *target*, wrapping around if necessary.\n\n    Examples:\n        >>> next_greatest_letter_v2([\"c\", \"f\", \"j\"], \"d\")\n        'f'\n    \"\"\"\n    for letter in letters:\n        if letter > target:\n            return letter\n    return letters[0]\n"
  },
  {
    "path": "algorithms/searching/search_insert.py",
    "content": "\"\"\"\nSearch Insert Position\n\nGiven a sorted array and a target value, return the index if the target is\nfound.  If not, return the index where it would be if it were inserted in\norder.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef search_insert(array: list[int], val: int) -> int:\n    \"\"\"Return the index of *val* or the position where it should be inserted.\n\n    Args:\n        array: Sorted list of integers in ascending order.\n        val: Value to search for or insert.\n\n    Returns:\n        Index of *val* in *array*, or the index at which *val* would be\n        inserted to keep *array* sorted.\n\n    Examples:\n        >>> search_insert([1, 3, 5, 6], 5)\n        2\n        >>> search_insert([1, 3, 5, 6], 2)\n        1\n        >>> search_insert([1, 3, 5, 6], 7)\n        4\n        >>> search_insert([1, 3, 5, 6], 0)\n        0\n    \"\"\"\n    low = 0\n    high = len(array) - 1\n    while low <= high:\n        mid = low + (high - low) // 2\n        if val > array[mid]:\n            low = mid + 1\n        else:\n            high = mid - 1\n    return low\n"
  },
  {
    "path": "algorithms/searching/search_range.py",
    "content": "\"\"\"\nSearch Range\n\nGiven a sorted array of integers and a target value, find the starting and\nending positions of the target.  Returns [-1, -1] if the target is not found.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(log n + k) where k is the number of occurrences in the worst\n           case for the backward scan, O(log n) for the initial search\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef search_range(nums: list[int], target: int) -> list[int]:\n    \"\"\"Find the first and last positions of *target* in *nums*.\n\n    Args:\n        nums: Sorted list of integers in ascending order.\n        target: Value to search for.\n\n    Returns:\n        A two-element list ``[first, last]`` of indices, or ``[-1, -1]``\n        if *target* is not present.\n\n    Examples:\n        >>> search_range([5, 7, 7, 8, 8, 8, 10], 8)\n        [3, 5]\n        >>> search_range([5, 7, 7, 8, 8, 8, 10], 11)\n        [-1, -1]\n    \"\"\"\n    low = 0\n    high = len(nums) - 1\n\n    while low < high:\n        mid = low + (high - low) // 2\n        if target <= nums[mid]:\n            high = mid\n        else:\n            low = mid + 1\n\n    for j in range(len(nums) - 1, -1, -1):\n        if nums[j] == target:\n            return [low, j]\n\n    return [-1, -1]\n"
  },
  {
    "path": "algorithms/searching/search_rotate.py",
    "content": "\"\"\"\nSearch in Rotated Sorted Array\n\nSearch for a target value in an array that was sorted in ascending order and\nthen rotated at some unknown pivot.  One half of the array is always in sorted\norder; we identify that half and decide which side to search.\n\nReference: https://en.wikipedia.org/wiki/Binary_search_algorithm\n\nComplexity:\n    Time:  O(1) best / O(log n) average / O(log n) worst\n    Space: O(1) iterative, O(log n) recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef search_rotate(array: list[int], val: int) -> int:\n    \"\"\"Search for *val* in a rotated sorted *array* (iterative).\n\n    Args:\n        array: A sorted list of integers that has been rotated at an\n            unknown pivot.\n        val: Value to search for.\n\n    Returns:\n        Index of *val* in *array*, or -1 if not found.\n\n    Examples:\n        >>> search_rotate([4, 5, 6, 7, 0, 1, 2], 0)\n        4\n        >>> search_rotate([4, 5, 6, 7, 0, 1, 2], 3)\n        -1\n    \"\"\"\n    low, high = 0, len(array) - 1\n    while low <= high:\n        mid = (low + high) // 2\n        if val == array[mid]:\n            return mid\n\n        if array[low] <= array[mid]:\n            if array[low] <= val <= array[mid]:\n                high = mid - 1\n            else:\n                low = mid + 1\n        else:\n            if array[mid] <= val <= array[high]:\n                low = mid + 1\n            else:\n                high = mid - 1\n\n    return -1\n\n\ndef search_rotate_recur(\n    array: list[int],\n    low: int,\n    high: int,\n    val: int,\n) -> int:\n    \"\"\"Search for *val* in a rotated sorted *array* (recursive).\n\n    Args:\n        array: A sorted list of integers that has been rotated at an\n            unknown pivot.\n        low: Lower bound index of the current search range.\n        high: Upper bound index of the current search range.\n        val: Value to search for.\n\n    Returns:\n        Index of *val* in *array*, or -1 if not found.\n\n    Examples:\n        >>> search_rotate_recur([4, 5, 6, 7, 0, 1, 2], 0, 6, 0)\n        4\n    \"\"\"\n    if low >= high:\n        return -1\n    mid = (low + high) // 2\n    if val == array[mid]:\n        return mid\n    if array[low] <= array[mid]:\n        if array[low] <= val <= array[mid]:\n            return search_rotate_recur(array, low, mid - 1, val)\n        return search_rotate_recur(array, mid + 1, high, val)\n    if array[mid] <= val <= array[high]:\n        return search_rotate_recur(array, mid + 1, high, val)\n    return search_rotate_recur(array, low, mid - 1, val)\n"
  },
  {
    "path": "algorithms/searching/sentinel_search.py",
    "content": "\"\"\"Sentinel linear search — a small optimisation over naive linear search.\n\nBy placing the target at the end of the array (as a sentinel), we can\nremove the bounds check from the inner loop, roughly halving comparisons.\n\nTime: O(n) — same asymptotic complexity but fewer comparisons in practice.\n\nInspired by PR #907 (Abhishek-Pashte).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef sentinel_search(arr: list[int], target: int) -> int:\n    \"\"\"Return the index of *target* in *arr*, or -1 if absent.\n\n    Modifies (and restores) the last element temporarily.\n    \"\"\"\n    n = len(arr)\n    if n == 0:\n        return -1\n    last = arr[-1]\n    arr[-1] = target\n    i = 0\n    while arr[i] != target:\n        i += 1\n    arr[-1] = last\n    if i < n - 1 or arr[-1] == target:\n        return i\n    return -1\n"
  },
  {
    "path": "algorithms/searching/ternary_search.py",
    "content": "\"\"\"\nTernary Search\n\nSearch for a target value in a sorted array by dividing the search range into\nthree equal parts instead of two.  At each step two midpoints are computed and\nthe search range is narrowed to one third.\n\nReference: https://en.wikipedia.org/wiki/Ternary_search\n\nComplexity:\n    Time:  O(1) best / O(log3 n) average / O(log3 n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef ternary_search(left: int, right: int, key: int, array: list[int]) -> int:\n    \"\"\"Search for *key* in a sorted *array* using ternary search.\n\n    Args:\n        left: Lower bound index of the search range (inclusive).\n        right: Upper bound index of the search range (inclusive).\n        key: Value to search for.\n        array: Sorted list of integers in ascending order.\n\n    Returns:\n        Index of *key* in *array*, or -1 if not found within the range.\n\n    Examples:\n        >>> ternary_search(0, 8, 5, [1, 2, 3, 4, 5, 6, 7, 8, 9])\n        4\n        >>> ternary_search(0, 4, 0, [1, 2, 3, 4, 5])\n        -1\n    \"\"\"\n    while right >= left:\n        mid1 = left + (right - left) // 3\n        mid2 = right - (right - left) // 3\n\n        if key == array[mid1]:\n            return mid1\n        if key == array[mid2]:\n            return mid2\n\n        if key < array[mid1]:\n            right = mid1 - 1\n        elif key > array[mid2]:\n            left = mid2 + 1\n        else:\n            left = mid1 + 1\n            right = mid2 - 1\n\n    return -1\n"
  },
  {
    "path": "algorithms/searching/two_sum.py",
    "content": "\"\"\"\nTwo Sum\n\nGiven a sorted array of integers and a target sum, find the 1-based indices of\nthe two numbers that add up to the target.  Three approaches are provided:\nbinary search, hash table, and two pointers.\n\nReference: https://en.wikipedia.org/wiki/Subset_sum_problem\n\nComplexity:\n    two_sum   -- O(n log n) time, O(1) space   (binary search)\n    two_sum1  -- O(n) time, O(n) space          (hash table)\n    two_sum2  -- O(n) time, O(1) space          (two pointers)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef two_sum(numbers: list[int], target: int) -> list[int] | None:\n    \"\"\"Find two numbers that add up to *target* using binary search.\n\n    Args:\n        numbers: Sorted list of integers in ascending order.\n        target: Desired sum of the two numbers.\n\n    Returns:\n        A list of two 1-based indices ``[i, j]`` such that\n        ``numbers[i-1] + numbers[j-1] == target``, or ``None`` if no\n        pair exists.\n\n    Examples:\n        >>> two_sum([2, 7, 11, 15], 9)\n        [1, 2]\n        >>> two_sum([1, 2, 3], 7) is None\n        True\n    \"\"\"\n    for i, number in enumerate(numbers):\n        second_val = target - number\n        low, high = i + 1, len(numbers) - 1\n        while low <= high:\n            mid = low + (high - low) // 2\n            if second_val == numbers[mid]:\n                return [i + 1, mid + 1]\n            if second_val > numbers[mid]:\n                low = mid + 1\n            else:\n                high = mid - 1\n    return None\n\n\ndef two_sum1(numbers: list[int], target: int) -> list[int] | None:\n    \"\"\"Find two numbers that add up to *target* using a hash table.\n\n    Args:\n        numbers: List of integers (need not be sorted).\n        target: Desired sum of the two numbers.\n\n    Returns:\n        A list of two 1-based indices ``[i, j]`` such that\n        ``numbers[i-1] + numbers[j-1] == target``, or ``None`` if no\n        pair exists.\n\n    Examples:\n        >>> two_sum1([2, 7, 11, 15], 9)\n        [1, 2]\n    \"\"\"\n    seen: dict[int, int] = {}\n    for i, num in enumerate(numbers):\n        if target - num in seen:\n            return [seen[target - num] + 1, i + 1]\n        seen[num] = i\n    return None\n\n\ndef two_sum2(numbers: list[int], target: int) -> list[int] | None:\n    \"\"\"Find two numbers that add up to *target* using two pointers.\n\n    Args:\n        numbers: Sorted list of integers in ascending order.\n        target: Desired sum of the two numbers.\n\n    Returns:\n        A list of two 1-based indices ``[i, j]`` such that\n        ``numbers[i-1] + numbers[j-1] == target``, or ``None`` if no\n        pair exists.\n\n    Examples:\n        >>> two_sum2([2, 7, 11, 15], 9)\n        [1, 2]\n    \"\"\"\n    left = 0\n    right = len(numbers) - 1\n    while left < right:\n        current_sum = numbers[left] + numbers[right]\n        if current_sum == target:\n            return [left + 1, right + 1]\n        if current_sum > target:\n            right = right - 1\n        else:\n            left = left + 1\n    return None\n"
  },
  {
    "path": "algorithms/set/__init__.py",
    "content": "from .find_keyboard_row import find_keyboard_row\nfrom .randomized_set import RandomizedSet\nfrom .set_covering import greedy_set_cover, optimal_set_cover\n\n__all__ = [\n    \"RandomizedSet\",\n    \"find_keyboard_row\",\n    \"greedy_set_cover\",\n    \"optimal_set_cover\",\n]\n"
  },
  {
    "path": "algorithms/set/find_keyboard_row.py",
    "content": "\"\"\"\nKeyboard Row Filter\n\nGiven a list of words, return the words that can be typed using letters from\nonly one row of an American QWERTY keyboard.\n\nReference: https://leetcode.com/problems/keyboard-row/description/\n\nComplexity:\n    Time:  O(n * m) where n is the number of words and m is average word length\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n_KEYBOARD_ROWS: list[set[str]] = [\n    set(\"qwertyuiop\"),\n    set(\"asdfghjkl\"),\n    set(\"zxcvbnm\"),\n]\n\n\ndef find_keyboard_row(words: list[str]) -> list[str]:\n    \"\"\"Return words that can be typed using one keyboard row.\n\n    Args:\n        words: A list of words to check.\n\n    Returns:\n        A list of words each typable on a single keyboard row.\n\n    Examples:\n        >>> find_keyboard_row([\"Hello\", \"Alaska\", \"Dad\", \"Peace\"])\n        ['Alaska', 'Dad']\n    \"\"\"\n    result: list[str] = []\n    for word in words:\n        for row in _KEYBOARD_ROWS:\n            if set(word.lower()).issubset(row):\n                result.append(word)\n    return result\n"
  },
  {
    "path": "algorithms/set/randomized_set.py",
    "content": "\"\"\"\nRandomized Set\n\nA data structure that supports insert, remove, and get-random-element\noperations, all in average O(1) time.\n\nReference: https://leetcode.com/problems/insert-delete-getrandom-o1/\n\nComplexity:\n    Time:  O(1) average for insert, remove, and random_element\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport random\n\n\nclass RandomizedSet:\n    \"\"\"A set supporting O(1) insert, remove, and random access.\"\"\"\n\n    def __init__(self) -> None:\n        self.elements: list[int] = []\n        self.index_map: dict[int, int] = {}\n\n    def insert(self, new_one: int) -> None:\n        \"\"\"Insert a value into the set if not already present.\n\n        Args:\n            new_one: The value to insert.\n        \"\"\"\n        if new_one in self.index_map:\n            return\n        self.index_map[new_one] = len(self.elements)\n        self.elements.append(new_one)\n\n    def remove(self, old_one: int) -> None:\n        \"\"\"Remove a value from the set if present.\n\n        Args:\n            old_one: The value to remove.\n        \"\"\"\n        if old_one not in self.index_map:\n            return\n        index = self.index_map[old_one]\n        last = self.elements.pop()\n        self.index_map.pop(old_one)\n        if index == len(self.elements):\n            return\n        self.elements[index] = last\n        self.index_map[last] = index\n\n    def random_element(self) -> int:\n        \"\"\"Return a random element from the set.\n\n        Returns:\n            A randomly chosen element.\n        \"\"\"\n        return random.choice(self.elements)\n"
  },
  {
    "path": "algorithms/set/set_covering.py",
    "content": "\"\"\"\nSet Cover Problem\n\nGiven a universe U of n elements, a collection S of subsets of U, and a cost\nfor each subset, find the minimum-cost sub-collection that covers all of U.\n\nReference: https://en.wikipedia.org/wiki/Set_cover_problem\n\nComplexity:\n    optimal_set_cover:\n        Time:  O(2^m * n) where m is the number of subsets\n        Space: O(2^m)\n    greedy_set_cover:\n        Time:  O(m * n)\n        Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom itertools import chain, combinations\n\n\ndef _powerset(iterable: list[str]) -> chain[tuple[str, ...]]:\n    \"\"\"Generate all subsets of the iterable.\n\n    Args:\n        iterable: Input collection.\n\n    Returns:\n        An iterator over all subsets (the power set).\n    \"\"\"\n    items = list(iterable)\n    return chain.from_iterable(combinations(items, r) for r in range(len(items) + 1))\n\n\ndef optimal_set_cover(\n    universe: set[int],\n    subsets: dict[str, set[int]],\n    costs: dict[str, int],\n) -> tuple[str, ...] | None:\n    \"\"\"Find the minimum-cost exact set cover via brute force.\n\n    Warning: O(2^m) complexity -- do not use on large inputs.\n\n    Args:\n        universe: The set of all elements to cover.\n        subsets: Mapping of subset name to its elements.\n        costs: Mapping of subset name to its cost.\n\n    Returns:\n        A tuple of subset names forming the optimal cover, or None if\n        no cover exists.\n\n    Examples:\n        >>> universe = {1, 2, 3, 4, 5}\n        >>> subsets = {'S1': {4, 1, 3}, 'S2': {2, 5}, 'S3': {1, 4, 3, 2}}\n        >>> costs = {'S1': 5, 'S2': 10, 'S3': 3}\n        >>> optimal_set_cover(universe, subsets, costs)\n        ('S2', 'S3')\n    \"\"\"\n    pset = _powerset(list(subsets.keys()))\n    best_set: tuple[str, ...] | None = None\n    best_cost = float(\"inf\")\n    for subset in pset:\n        covered: set[int] = set()\n        cost = 0\n        for name in subset:\n            covered.update(subsets[name])\n            cost += costs[name]\n        if len(covered) == len(universe) and cost < best_cost:\n            best_set = subset\n            best_cost = cost\n    return best_set\n\n\ndef greedy_set_cover(\n    universe: set[int],\n    subsets: dict[str, set[int]],\n    costs: dict[str, int],\n) -> list[str] | None:\n    \"\"\"Find an approximate set cover using a greedy approach.\n\n    Args:\n        universe: The set of all elements to cover.\n        subsets: Mapping of subset name to its elements.\n        costs: Mapping of subset name to its cost.\n\n    Returns:\n        A list of subset names forming the greedy cover, or None if the\n        subsets do not cover the universe.\n\n    Examples:\n        >>> universe = {1, 2, 3, 4, 5}\n        >>> subsets = {'S1': {4, 1, 3}, 'S2': {2, 5}, 'S3': {1, 4, 3, 2}}\n        >>> costs = {'S1': 5, 'S2': 10, 'S3': 3}\n        >>> greedy_set_cover(universe, subsets, costs)\n        ['S3', 'S2']\n    \"\"\"\n    all_elements = set(e for s in subsets.values() for e in s)\n    if all_elements != universe:\n        return None\n\n    covered: set[int] = set()\n    cover_sets: list[str] = []\n\n    while covered != universe:\n        min_cost_elem_ratio = float(\"inf\")\n        min_set: str | None = None\n        for name, elements in subsets.items():\n            new_elements = len(elements - covered)\n            if new_elements != 0:\n                cost_elem_ratio = costs[name] / new_elements\n                if cost_elem_ratio < min_cost_elem_ratio:\n                    min_cost_elem_ratio = cost_elem_ratio\n                    min_set = name\n        cover_sets.append(min_set)\n        covered |= subsets[min_set]\n    return cover_sets\n"
  },
  {
    "path": "algorithms/sorting/__init__.py",
    "content": "\"\"\"Sorting algorithms.\"\"\"\n\nfrom algorithms.sorting.bead_sort import bead_sort\nfrom algorithms.sorting.bitonic_sort import bitonic_sort\nfrom algorithms.sorting.bogo_sort import bogo_sort\nfrom algorithms.sorting.bubble_sort import bubble_sort\nfrom algorithms.sorting.bucket_sort import bucket_sort\nfrom algorithms.sorting.cocktail_shaker_sort import cocktail_shaker_sort\nfrom algorithms.sorting.comb_sort import comb_sort\nfrom algorithms.sorting.counting_sort import counting_sort\nfrom algorithms.sorting.cycle_sort import cycle_sort\nfrom algorithms.sorting.exchange_sort import exchange_sort\nfrom algorithms.sorting.gnome_sort import gnome_sort\nfrom algorithms.sorting.heap_sort import max_heap_sort, min_heap_sort\nfrom algorithms.sorting.insertion_sort import insertion_sort\nfrom algorithms.sorting.meeting_rooms import can_attend_meetings\nfrom algorithms.sorting.merge_sort import merge_sort\nfrom algorithms.sorting.pancake_sort import pancake_sort\nfrom algorithms.sorting.pigeonhole_sort import pigeonhole_sort\nfrom algorithms.sorting.quick_sort import quick_sort\nfrom algorithms.sorting.radix_sort import radix_sort\nfrom algorithms.sorting.selection_sort import selection_sort\nfrom algorithms.sorting.shell_sort import shell_sort\nfrom algorithms.sorting.sort_colors import sort_colors\nfrom algorithms.sorting.stooge_sort import stooge_sort\nfrom algorithms.sorting.wiggle_sort import wiggle_sort\n\n__all__ = [\n    \"bead_sort\",\n    \"bitonic_sort\",\n    \"bogo_sort\",\n    \"bubble_sort\",\n    \"bucket_sort\",\n    \"can_attend_meetings\",\n    \"cocktail_shaker_sort\",\n    \"comb_sort\",\n    \"counting_sort\",\n    \"cycle_sort\",\n    \"exchange_sort\",\n    \"gnome_sort\",\n    \"insertion_sort\",\n    \"max_heap_sort\",\n    \"merge_sort\",\n    \"min_heap_sort\",\n    \"pancake_sort\",\n    \"pigeonhole_sort\",\n    \"quick_sort\",\n    \"radix_sort\",\n    \"selection_sort\",\n    \"shell_sort\",\n    \"sort_colors\",\n    \"stooge_sort\",\n    \"wiggle_sort\",\n]\n"
  },
  {
    "path": "algorithms/sorting/bead_sort.py",
    "content": "\"\"\"\nBead Sort\n\nBead sort (also known as gravity sort) simulates how beads settle under\ngravity on an abacus.  It only works with non-negative integers.\n\nReference: https://en.wikipedia.org/wiki/Bead_sort\n\nComplexity:\n    Time:  O(n) best / O(n * max_value) average / O(n * max_value) worst\n    Space: O(n * max_value)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef bead_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array of non-negative integers using bead sort.\n\n    Args:\n        array: List of non-negative integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Raises:\n        ValueError: If any element is negative.\n\n    Examples:\n        >>> bead_sort([6, 3, 4, 1, 5, 2])\n        [1, 2, 3, 4, 5, 6]\n    \"\"\"\n    if any(num < 0 for num in array):\n        raise ValueError(\"Bead sort only works with non-negative integers.\")\n\n    max_value = max(array) if array else 0\n    grid = [[0] * len(array) for _ in range(max_value)]\n\n    # Drop beads (place beads in columns)\n    for col, num in enumerate(array):\n        for row in range(num):\n            grid[row][col] = 1\n\n    # Let the beads \"fall\" (count beads in each row)\n    for row in grid:\n        bead_count = sum(row)\n        for col in range(len(array)):\n            row[col] = 1 if col < bead_count else 0\n\n    # Read sorted values from the grid (rightmost column has fewest beads)\n    n = len(array)\n    sorted_array = [0] * n\n    for col in range(n):\n        sorted_array[col] = sum(grid[row][n - 1 - col] for row in range(max_value))\n    return sorted_array\n"
  },
  {
    "path": "algorithms/sorting/bitonic_sort.py",
    "content": "\"\"\"\nBitonic Sort\n\nBitonic sort is a comparison-based sorting algorithm designed for parallel\nexecution.  This implementation is sequential.  The input size must be a\npower of two.\n\nReference: https://en.wikipedia.org/wiki/Bitonic_sorter\n\nComplexity:\n    Time:  O(n log^2 n) best / O(n log^2 n) average / O(n log^2 n) worst\n    Space: O(n log^2 n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef bitonic_sort(array: list[int], reverse: bool = False) -> list[int]:\n    \"\"\"Sort an array using bitonic sort.\n\n    Args:\n        array:   List of integers to sort.\n        reverse: If True, sort in descending order.\n\n    Returns:\n        A sorted list.\n\n    Raises:\n        ValueError: If the array length is not a power of two.\n\n    Examples:\n        >>> bitonic_sort([4, 2, 1, 3])\n        [1, 2, 3, 4]\n    \"\"\"\n    n = len(array)\n    if n <= 1:\n        return array\n    if not (n and not (n & (n - 1))):\n        raise ValueError(\"the size of input should be power of two\")\n\n    left = bitonic_sort(array[: n // 2], True)\n    right = bitonic_sort(array[n // 2 :], False)\n    return _bitonic_merge(left + right, reverse)\n\n\ndef _compare(array: list[int], reverse: bool) -> list[int]:\n    \"\"\"Compare and swap elements across the two halves of *array*.\"\"\"\n    half = len(array) // 2\n    for i in range(half):\n        if reverse != (array[i] > array[i + half]):\n            array[i], array[i + half] = array[i + half], array[i]\n    return array\n\n\ndef _bitonic_merge(array: list[int], reverse: bool) -> list[int]:\n    \"\"\"Recursively merge a bitonic sequence into sorted order.\"\"\"\n    n = len(array)\n    if n <= 1:\n        return array\n\n    array = _compare(array, reverse)\n    left = _bitonic_merge(array[: n // 2], reverse)\n    right = _bitonic_merge(array[n // 2 :], reverse)\n    return left + right\n"
  },
  {
    "path": "algorithms/sorting/bogo_sort.py",
    "content": "\"\"\"\nBogo Sort\n\nBogo sort repeatedly shuffles the array at random until it happens to be\nsorted.  It is extremely inefficient and used only for educational purposes.\n\nReference: https://en.wikipedia.org/wiki/Bogosort\n\nComplexity:\n    Time:  O(n) best / O(n * n!) average / O(infinity) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport random\n\n\ndef bogo_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using bogo sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> bogo_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    while not _is_sorted(array):\n        random.shuffle(array)\n    return array\n\n\ndef _is_sorted(array: list[int]) -> bool:\n    \"\"\"Return True if *array* is in non-decreasing order.\"\"\"\n    return all(\n        array[i] <= array[i + 1] for i in range(len(array) - 1)\n    )\n"
  },
  {
    "path": "algorithms/sorting/bubble_sort.py",
    "content": "\"\"\"\nBubble Sort\n\nBubble sort repeatedly steps through the list, compares adjacent elements\nand swaps them if they are in the wrong order.\n\nReference: https://en.wikipedia.org/wiki/Bubble_sort\n\nComplexity:\n    Time:  O(n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef bubble_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using bubble sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> bubble_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    swapped = True\n    passes = 0\n    while swapped:\n        swapped = False\n        for i in range(1, n - passes):\n            if array[i - 1] > array[i]:\n                array[i - 1], array[i] = array[i], array[i - 1]\n                swapped = True\n        passes += 1\n    return array\n"
  },
  {
    "path": "algorithms/sorting/bucket_sort.py",
    "content": "\"\"\"\nBucket Sort\n\nBucket sort distributes elements into a number of buckets, sorts each\nbucket individually (here using insertion sort), and then concatenates\nall buckets.\n\nReference: https://en.wikipedia.org/wiki/Bucket_sort\n\nComplexity:\n    Time:  O(n + k) best / O(n + k) average / O(n^2) worst\n    Space: O(n + k)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef bucket_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using bucket sort.\n\n    Args:\n        array: List of non-negative integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> bucket_sort([3, 1, 2, 4])\n        [1, 2, 3, 4]\n    \"\"\"\n    num_buckets = len(array)\n    buckets: list[list[int]] = [[] for _ in range(num_buckets)]\n\n    max_value = max(array) + 1\n    for value in array:\n        index = value * num_buckets // max_value\n        buckets[index].append(value)\n\n    sorted_list: list[int] = []\n    for bucket in buckets:\n        sorted_list.extend(_insertion_sort(bucket))\n    return sorted_list\n\n\ndef _insertion_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort *array* in-place using insertion sort and return it.\"\"\"\n    for i in range(1, len(array)):\n        key = array[i]\n        j = i - 1\n        while j >= 0 and array[j] > key:\n            array[j + 1] = array[j]\n            j -= 1\n        array[j + 1] = key\n    return array\n"
  },
  {
    "path": "algorithms/sorting/cocktail_shaker_sort.py",
    "content": "\"\"\"\nCocktail Shaker Sort\n\nCocktail shaker sort is a variation of bubble sort that traverses the\nlist alternately from left-to-right and right-to-left.\n\nReference: https://en.wikipedia.org/wiki/Cocktail_shaker_sort\n\nComplexity:\n    Time:  O(n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef cocktail_shaker_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using cocktail shaker sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> cocktail_shaker_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    swapped = True\n    while swapped:\n        swapped = False\n        for i in range(1, n):\n            if array[i - 1] > array[i]:\n                array[i - 1], array[i] = array[i], array[i - 1]\n                swapped = True\n        if not swapped:\n            return array\n        swapped = False\n        for i in range(n - 1, 0, -1):\n            if array[i - 1] > array[i]:\n                array[i - 1], array[i] = array[i], array[i - 1]\n                swapped = True\n    return array\n"
  },
  {
    "path": "algorithms/sorting/comb_sort.py",
    "content": "\"\"\"\nComb Sort\n\nComb sort improves on bubble sort by using a gap sequence that shrinks by\na factor of approximately 1.3 on each pass, eliminating small values near\nthe end of the list (known as \"turtles\") more quickly.\n\nReference: https://en.wikipedia.org/wiki/Comb_sort\n\nComplexity:\n    Time:  O(n log n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef comb_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using comb sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> comb_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    gap = n\n    shrink_factor = 1.3\n    is_sorted = False\n\n    while not is_sorted:\n        gap = int(gap / shrink_factor)\n        if gap <= 1:\n            gap = 1\n            is_sorted = True\n\n        i = 0\n        while i + gap < n:\n            if array[i] > array[i + gap]:\n                array[i], array[i + gap] = array[i + gap], array[i]\n                is_sorted = False\n            i += 1\n\n    return array\n"
  },
  {
    "path": "algorithms/sorting/counting_sort.py",
    "content": "\"\"\"\nCounting Sort\n\nCounting sort counts the occurrences of each value and uses cumulative\ncounts to place each element in its correct position.  It supports\nnegative integers by shifting values internally.\n\nReference: https://en.wikipedia.org/wiki/Counting_sort\n\nComplexity:\n    Time:  O(n + k) best / O(n + k) average / O(n + k) worst\n    Space: O(n + k)   where k is the range of input values\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef counting_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using counting sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> counting_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    min_value = min(array)\n    offset = -min_value if min_value < 0 else 0\n\n    shifted = [v + offset for v in array]\n    max_value = max(shifted)\n\n    counts = [0] * (max_value + 1)\n    for value in shifted:\n        counts[value] += 1\n\n    # Build cumulative counts\n    for i in range(1, max_value + 1):\n        counts[i] += counts[i - 1]\n\n    result = [0] * len(array)\n    for i in range(len(array) - 1, -1, -1):\n        value = shifted[i]\n        counts[value] -= 1\n        result[counts[value]] = value - offset\n    return result\n"
  },
  {
    "path": "algorithms/sorting/cycle_sort.py",
    "content": "\"\"\"\nCycle Sort\n\nCycle sort decomposes the permutation into cycles and rotates each cycle\nto produce a sorted result.  It minimises the number of writes to the\narray, making it useful when writes are expensive.\n\nReference: https://en.wikipedia.org/wiki/Cycle_sort\n\nComplexity:\n    Time:  O(n^2) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef cycle_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using cycle sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> cycle_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    length = len(array)\n\n    for start in range(length - 1):\n        item = array[start]\n\n        # Count how many elements are smaller to find the correct position\n        position = start\n        for i in range(start + 1, length):\n            if array[i] < item:\n                position += 1\n\n        # No cycle needed for this element\n        if position == start:\n            continue\n\n        # Skip duplicates\n        while item == array[position]:\n            position += 1\n        array[position], item = item, array[position]\n\n        # Rotate the rest of the cycle\n        while position != start:\n            position = start\n            for i in range(start + 1, length):\n                if array[i] < item:\n                    position += 1\n\n            while item == array[position]:\n                position += 1\n            array[position], item = item, array[position]\n\n    return array\n"
  },
  {
    "path": "algorithms/sorting/exchange_sort.py",
    "content": "\"\"\"\nExchange Sort\n\nExchange sort compares every pair of elements and swaps them if they are\nout of order.  It is conceptually similar to bubble sort.\n\nReference: https://en.wikipedia.org/wiki/Sorting_algorithm#Exchange_sort\n\nComplexity:\n    Time:  O(n^2) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef exchange_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using exchange sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> exchange_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    for i in range(n - 1):\n        for j in range(i + 1, n):\n            if array[i] > array[j]:\n                array[i], array[j] = array[j], array[i]\n    return array\n"
  },
  {
    "path": "algorithms/sorting/gnome_sort.py",
    "content": "\"\"\"\nGnome Sort\n\nGnome sort moves an element toward the front of the list until it finds\nan element that is smaller or equal, then steps forward again.  It is\nsimilar to insertion sort but uses swaps instead of shifts.\n\nReference: https://en.wikipedia.org/wiki/Gnome_sort\n\nComplexity:\n    Time:  O(n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef gnome_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using gnome sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> gnome_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    index = 0\n    while index < n:\n        if index == 0 or array[index] >= array[index - 1]:\n            index += 1\n        else:\n            array[index], array[index - 1] = array[index - 1], array[index]\n            index -= 1\n    return array\n"
  },
  {
    "path": "algorithms/sorting/heap_sort.py",
    "content": "\"\"\"\nHeap Sort\n\nHeap sort builds a heap from the data and repeatedly extracts the\nextreme element to produce a sorted array.  Two variants are provided:\nmax-heap sort and min-heap sort.\n\nReference: https://en.wikipedia.org/wiki/Heapsort\n\nComplexity:\n    Time:  O(n log n) best / O(n log n) average / O(n log n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef max_heap_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using a max-heap.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> max_heap_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    for i in range(len(array) - 1, 0, -1):\n        _max_heapify(array, i)\n    return array\n\n\ndef _max_heapify(array: list[int], end: int) -> None:\n    \"\"\"Build a max-heap on *array[0..end]* and swap the root to *end*.\"\"\"\n    last_parent = (end - 1) // 2\n\n    for parent in range(last_parent, -1, -1):\n        current = parent\n        while current <= last_parent:\n            child = 2 * current + 1\n            if child + 1 <= end and array[child] < array[child + 1]:\n                child += 1\n            if array[child] > array[current]:\n                array[current], array[child] = array[child], array[current]\n                current = child\n            else:\n                break\n\n    array[0], array[end] = array[end], array[0]\n\n\ndef min_heap_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using a min-heap.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> min_heap_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    for i in range(len(array) - 1):\n        _min_heapify(array, i)\n    return array\n\n\ndef _min_heapify(array: list[int], start: int) -> None:\n    \"\"\"Build a min-heap on *array[start..]* and place the minimum at *start*.\"\"\"\n    end = len(array) - 1\n    last_parent = (end - start - 1) // 2\n\n    for parent in range(last_parent, -1, -1):\n        current = parent\n        while current <= last_parent:\n            child = 2 * current + 1\n            if (\n                child + 1 <= end - start\n                and array[child + start] > array[child + 1 + start]\n            ):\n                child += 1\n            if array[child + start] < array[current + start]:\n                array[current + start], array[child + start] = (\n                    array[child + start],\n                    array[current + start],\n                )\n                current = child\n            else:\n                break\n"
  },
  {
    "path": "algorithms/sorting/insertion_sort.py",
    "content": "\"\"\"\nInsertion Sort\n\nInsertion sort builds the sorted list one element at a time by repeatedly\npicking the next element and inserting it into its correct position.\n\nReference: https://en.wikipedia.org/wiki/Insertion_sort\n\nComplexity:\n    Time:  O(n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef insertion_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using insertion sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> insertion_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    for i in range(len(array)):\n        cursor = array[i]\n        pos = i\n\n        while pos > 0 and array[pos - 1] > cursor:\n            array[pos] = array[pos - 1]\n            pos -= 1\n        array[pos] = cursor\n\n    return array\n"
  },
  {
    "path": "algorithms/sorting/meeting_rooms.py",
    "content": "\"\"\"\nMeeting Rooms\n\nGiven an array of meeting time intervals consisting of start and end times\n[[s1, e1], [s2, e2], ...] (si < ei), determine if a person could attend\nall meetings (i.e. no two meetings overlap).\n\nReference: https://leetcode.com/problems/meeting-rooms/\n\nComplexity:\n    Time:  O(n log n) best / O(n log n) average / O(n log n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef can_attend_meetings(intervals: list) -> bool:\n    \"\"\"Determine whether all meetings can be attended without overlap.\n\n    Args:\n        intervals: List of interval objects with *start* and *end* attributes.\n\n    Returns:\n        True if a person can attend all meetings, False otherwise.\n\n    Examples:\n        >>> # With intervals [[0,30],[5,10],[15,20]] the answer is False.\n        >>> # With intervals [[7,10],[2,4]] the answer is True.\n    \"\"\"\n    intervals = sorted(intervals, key=lambda x: x.start)\n    for i in range(1, len(intervals)):\n        if intervals[i].start < intervals[i - 1].end:\n            return False\n    return True\n"
  },
  {
    "path": "algorithms/sorting/merge_sort.py",
    "content": "\"\"\"\nMerge Sort\n\nMerge sort divides the array in half, recursively sorts each half, and\nthen merges the two sorted halves back together.\n\nReference: https://en.wikipedia.org/wiki/Merge_sort\n\nComplexity:\n    Time:  O(n log n) best / O(n log n) average / O(n log n) worst\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef merge_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using merge sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> merge_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    if len(array) <= 1:\n        return array\n\n    mid = len(array) // 2\n    left = merge_sort(array[:mid])\n    right = merge_sort(array[mid:])\n\n    _merge(left, right, array)\n    return array\n\n\ndef _merge(left: list[int], right: list[int], merged: list[int]) -> None:\n    \"\"\"Merge two sorted lists into *merged* in-place.\n\n    Args:\n        left:   Sorted left half.\n        right:  Sorted right half.\n        merged: Destination list (length == len(left) + len(right)).\n    \"\"\"\n    left_cursor = 0\n    right_cursor = 0\n\n    while left_cursor < len(left) and right_cursor < len(right):\n        if left[left_cursor] <= right[right_cursor]:\n            merged[left_cursor + right_cursor] = left[left_cursor]\n            left_cursor += 1\n        else:\n            merged[left_cursor + right_cursor] = right[right_cursor]\n            right_cursor += 1\n\n    for left_cursor in range(left_cursor, len(left)):  # noqa: B020\n        merged[left_cursor + right_cursor] = left[left_cursor]\n\n    for right_cursor in range(right_cursor, len(right)):  # noqa: B020\n        merged[left_cursor + right_cursor] = right[right_cursor]\n"
  },
  {
    "path": "algorithms/sorting/pancake_sort.py",
    "content": "\"\"\"\nPancake Sort\n\nPancake sort sorts an array by repeatedly finding the maximum element in\nthe unsorted portion, flipping it to the front, and then flipping the\nentire unsorted portion so the maximum lands at the end.\n\nReference: https://en.wikipedia.org/wiki/Pancake_sorting\n\nComplexity:\n    Time:  O(n) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef pancake_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using pancake sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> pancake_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    if len(array) <= 1:\n        return array\n\n    for cur in range(len(array), 1, -1):\n        index_max = array.index(max(array[0:cur]))\n        if index_max + 1 != cur:\n            if index_max != 0:\n                array[: index_max + 1] = reversed(array[: index_max + 1])\n            array[:cur] = reversed(array[:cur])\n    return array\n"
  },
  {
    "path": "algorithms/sorting/pigeonhole_sort.py",
    "content": "\"\"\"\nPigeonhole Sort\n\nPigeonhole sort is suitable for sorting lists where the number of\nelements and the range of possible key values are approximately equal.\n\nReference: https://en.wikipedia.org/wiki/Pigeonhole_sort\n\nComplexity:\n    Time:  O(n + range) best / O(n + range) average / O(n + range) worst\n    Space: O(range)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef pigeonhole_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using pigeonhole sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> pigeonhole_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    max_value = max(array)\n    min_value = min(array)\n    size = max_value - min_value + 1\n\n    holes = [0] * size\n    for value in array:\n        holes[value - min_value] += 1\n\n    i = 0\n    for count in range(size):\n        while holes[count] > 0:\n            holes[count] -= 1\n            array[i] = count + min_value\n            i += 1\n    return array\n"
  },
  {
    "path": "algorithms/sorting/quick_sort.py",
    "content": "\"\"\"\nQuick Sort\n\nQuick sort selects a pivot element, partitions the array around the\npivot, and recursively sorts the two partitions.\n\nReference: https://en.wikipedia.org/wiki/Quicksort\n\nComplexity:\n    Time:  O(n log n) best / O(n log n) average / O(n^2) worst\n    Space: O(log n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef quick_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using quick sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> quick_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    _quick_sort_recursive(array, 0, len(array) - 1)\n    return array\n\n\ndef _quick_sort_recursive(array: list[int], first: int, last: int) -> None:\n    \"\"\"Recursively sort *array[first..last]* in-place.\"\"\"\n    if first < last:\n        pivot = _partition(array, first, last)\n        _quick_sort_recursive(array, first, pivot - 1)\n        _quick_sort_recursive(array, pivot + 1, last)\n\n\ndef _partition(array: list[int], first: int, last: int) -> int:\n    \"\"\"Partition *array[first..last]* using the last element as pivot.\n\n    Returns:\n        The final index of the pivot element.\n    \"\"\"\n    wall = first\n    for pos in range(first, last):\n        if array[pos] < array[last]:\n            array[pos], array[wall] = array[wall], array[pos]\n            wall += 1\n    array[wall], array[last] = array[last], array[wall]\n    return wall\n"
  },
  {
    "path": "algorithms/sorting/radix_sort.py",
    "content": "\"\"\"\nRadix Sort\n\nRadix sort processes digits from least significant to most significant,\ndistributing elements into buckets for each digit and collecting them\nback in order.\n\nReference: https://en.wikipedia.org/wiki/Radix_sort\n\nComplexity:\n    Time:  O(n * k) best / O(n * k) average / O(n * k) worst\n    Space: O(n + k)   where k is the number of digits in the largest value\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef radix_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array of non-negative integers using radix sort.\n\n    Args:\n        array: List of non-negative integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> radix_sort([170, 45, 75, 90, 802, 24, 2, 66])\n        [2, 24, 45, 66, 75, 90, 170, 802]\n    \"\"\"\n    position = 1\n    max_number = max(array)\n\n    while position <= max_number:\n        buckets: list[list[int]] = [[] for _ in range(10)]\n\n        for num in array:\n            digit = num // position % 10\n            buckets[digit].append(num)\n\n        index = 0\n        for bucket in buckets:\n            for num in bucket:\n                array[index] = num\n                index += 1\n\n        position *= 10\n\n    return array\n"
  },
  {
    "path": "algorithms/sorting/selection_sort.py",
    "content": "\"\"\"\nSelection Sort\n\nSelection sort repeatedly selects the smallest element from the unsorted\nportion and moves it to the end of the sorted portion.\n\nReference: https://en.wikipedia.org/wiki/Selection_sort\n\nComplexity:\n    Time:  O(n^2) best / O(n^2) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef selection_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using selection sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> selection_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    for i in range(len(array)):\n        minimum = i\n        for j in range(i + 1, len(array)):\n            if array[j] < array[minimum]:\n                minimum = j\n        array[minimum], array[i] = array[i], array[minimum]\n    return array\n"
  },
  {
    "path": "algorithms/sorting/shell_sort.py",
    "content": "\"\"\"\nShell Sort\n\nShell sort is a generalisation of insertion sort that allows the exchange\nof elements that are far apart.  The gap between compared elements is\ngradually reduced until it becomes 1, at which point the algorithm\nbehaves like a standard insertion sort.\n\nReference: https://en.wikipedia.org/wiki/Shellsort\n\nComplexity:\n    Time:  O(n log n) best / O(n^(4/3)) average / O(n^2) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef shell_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using shell sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> shell_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    n = len(array)\n    gap = n // 2\n\n    while gap > 0:\n        y_index = gap\n        while y_index < n:\n            y = array[y_index]\n            x_index = y_index - gap\n            while x_index >= 0 and y < array[x_index]:\n                array[x_index + gap] = array[x_index]\n                x_index -= gap\n            array[x_index + gap] = y\n            y_index += 1\n        gap //= 2\n\n    return array\n"
  },
  {
    "path": "algorithms/sorting/sort_colors.py",
    "content": "\"\"\"\nSort Colors (Dutch National Flag)\n\nGiven an array with n objects colored red, white, or blue (represented by\n0, 1, and 2), sort them in-place so that objects of the same color are\nadjacent, with the colors in order red, white, blue.\n\nReference: https://leetcode.com/problems/sort-colors/\n\nComplexity:\n    Time:  O(n) best / O(n) average / O(n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef sort_colors(array: list[int]) -> list[int]:\n    \"\"\"Sort an array of 0s, 1s, and 2s in-place.\n\n    Args:\n        array: List of integers (each 0, 1, or 2) to sort.\n\n    Returns:\n        The sorted list.\n\n    Examples:\n        >>> sort_colors([2, 0, 1, 2, 1, 0])\n        [0, 0, 1, 1, 2, 2]\n    \"\"\"\n    red = white = 0\n    for k in range(len(array)):\n        value = array[k]\n        array[k] = 2\n        if value < 2:\n            array[white] = 1\n            white += 1\n        if value == 0:\n            array[red] = 0\n            red += 1\n    return array\n"
  },
  {
    "path": "algorithms/sorting/stooge_sort.py",
    "content": "\"\"\"\nStooge Sort\n\nStooge sort is a recursive sorting algorithm notable for its unusually\nbad time complexity.  It works by recursively sorting the first 2/3, then\nthe last 2/3, and then the first 2/3 again.\n\nReference: https://en.wikipedia.org/wiki/Stooge_sort\n\nComplexity:\n    Time:  O(n^2.709) best / O(n^2.709) average / O(n^2.709) worst\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef stooge_sort(array: list[int]) -> list[int]:\n    \"\"\"Sort an array in ascending order using stooge sort.\n\n    Args:\n        array: List of integers to sort.\n\n    Returns:\n        A sorted list.\n\n    Examples:\n        >>> stooge_sort([3, 1, 2])\n        [1, 2, 3]\n    \"\"\"\n    _stooge_sort(array, 0, len(array) - 1)\n    return array\n\n\ndef _stooge_sort(array: list[int], low: int, high: int) -> None:\n    \"\"\"Recursively sort *array[low..high]* using stooge sort.\"\"\"\n    if low >= high:\n        return\n\n    if array[low] > array[high]:\n        array[low], array[high] = array[high], array[low]\n\n    if high - low + 1 > 2:\n        third = (high - low + 1) // 3\n        _stooge_sort(array, low, high - third)\n        _stooge_sort(array, low + third, high)\n        _stooge_sort(array, low, high - third)\n"
  },
  {
    "path": "algorithms/sorting/wiggle_sort.py",
    "content": "\"\"\"\nWiggle Sort\n\nGiven an unsorted array, reorder it in-place such that\nnums[0] < nums[1] > nums[2] < nums[3] ...\n\nReference: https://leetcode.com/problems/wiggle-sort/\n\nComplexity:\n    Time:  O(n) best / O(n) average / O(n) worst\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef wiggle_sort(array: list[int]) -> list[int]:\n    \"\"\"Reorder *array* in-place into wiggle-sorted order.\n\n    Args:\n        array: List of integers to reorder.\n\n    Returns:\n        The wiggle-sorted list.\n\n    Examples:\n        >>> wiggle_sort([3, 5, 2, 1, 6, 4])\n        [3, 5, 1, 6, 2, 4]\n    \"\"\"\n    for i in range(len(array)):\n        if (i % 2 == 1) == (array[i - 1] > array[i]):\n            array[i - 1], array[i] = array[i], array[i - 1]\n    return array\n"
  },
  {
    "path": "algorithms/stack/__init__.py",
    "content": "\"\"\"Stack-based algorithm implementations.\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.data_structures.stack import (\n    AbstractStack,\n    ArrayStack,\n    LinkedListStack,\n    StackNode,\n)\n\nfrom .is_consecutive import first_is_consecutive, second_is_consecutive\nfrom .is_sorted import is_sorted\nfrom .longest_abs_path import length_longest_path\nfrom .ordered_stack import OrderedStack\nfrom .remove_min import remove_min\nfrom .simplify_path import simplify_path\nfrom .stutter import first_stutter, second_stutter\nfrom .switch_pairs import first_switch_pairs, second_switch_pairs\nfrom .valid_parenthesis import is_valid\n\n__all__ = [\n    \"AbstractStack\",\n    \"ArrayStack\",\n    \"LinkedListStack\",\n    \"OrderedStack\",\n    \"StackNode\",\n    \"first_is_consecutive\",\n    \"first_stutter\",\n    \"first_switch_pairs\",\n    \"is_sorted\",\n    \"is_valid\",\n    \"length_longest_path\",\n    \"remove_min\",\n    \"second_is_consecutive\",\n    \"second_stutter\",\n    \"second_switch_pairs\",\n    \"simplify_path\",\n]\n"
  },
  {
    "path": "algorithms/stack/is_consecutive.py",
    "content": "\"\"\"\nIs Consecutive\n\nCheck whether a stack contains a sequence of consecutive integers\nstarting from the bottom. Two approaches are provided: one using an\nauxiliary stack, and one using an auxiliary queue.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef first_is_consecutive(stack: list[int]) -> bool:\n    \"\"\"Check if a stack has consecutive integers using an auxiliary stack.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        True if the values are consecutive from bottom to top.\n\n    Examples:\n        >>> first_is_consecutive([3, 4, 5, 6, 7])\n        True\n        >>> first_is_consecutive([3, 4, 6, 7])\n        False\n    \"\"\"\n    storage_stack: list[int] = []\n    for _ in range(len(stack)):\n        first_value = stack.pop()\n        if len(stack) == 0:\n            return True\n        second_value = stack.pop()\n        if first_value - second_value != 1:\n            return False\n        stack.append(second_value)\n        storage_stack.append(first_value)\n\n    for _ in range(len(storage_stack)):\n        stack.append(storage_stack.pop())\n    return True\n\n\ndef second_is_consecutive(stack: list[int]) -> bool:\n    \"\"\"Check if a stack has consecutive integers using an auxiliary queue.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        True if the values are consecutive from bottom to top.\n\n    Examples:\n        >>> second_is_consecutive([3, 4, 5, 6, 7])\n        True\n        >>> second_is_consecutive([3, 4, 6, 7])\n        False\n    \"\"\"\n    queue: collections.deque[int] = collections.deque()\n    for _ in range(len(stack)):\n        first_value = stack.pop()\n        if len(stack) == 0:\n            return True\n        second_value = stack.pop()\n        if first_value - second_value != 1:\n            return False\n        stack.append(second_value)\n        queue.append(first_value)\n\n    for _ in range(len(queue)):\n        stack.append(queue.pop())\n    for _ in range(len(stack)):\n        queue.append(stack.pop())\n    for _ in range(len(queue)):\n        stack.append(queue.pop())\n\n    return True\n"
  },
  {
    "path": "algorithms/stack/is_sorted.py",
    "content": "\"\"\"\nIs Sorted\n\nCheck whether a stack is sorted in ascending order from bottom to top\nusing a single auxiliary stack.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_sorted(stack: list[int]) -> bool:\n    \"\"\"Check if a stack is sorted in ascending order (bottom to top).\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        True if sorted in ascending order, False otherwise.\n\n    Examples:\n        >>> is_sorted([1, 2, 3, 4, 5, 6])\n        True\n        >>> is_sorted([6, 3, 5, 1, 2, 4])\n        False\n    \"\"\"\n    storage_stack: list[int] = []\n    for _ in range(len(stack)):\n        if len(stack) == 0:\n            break\n        first_val = stack.pop()\n        if len(stack) == 0:\n            break\n        second_val = stack.pop()\n        if first_val < second_val:\n            return False\n        storage_stack.append(first_val)\n        stack.append(second_val)\n\n    for _ in range(len(storage_stack)):\n        stack.append(storage_stack.pop())\n\n    return True\n"
  },
  {
    "path": "algorithms/stack/longest_abs_path.py",
    "content": "\"\"\"\nLongest Absolute File Path\n\nGiven a string representing a file system in a special format, find the\nlength of the longest absolute path to a file. Directories and files\nare separated by newlines; depth is indicated by tab characters.\n\nReference: https://leetcode.com/problems/longest-absolute-file-path/\n\nComplexity:\n    Time:  O(n)\n    Space: O(d) where d is the maximum depth\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef length_longest_path(input_str: str) -> int:\n    \"\"\"Find the length of the longest absolute path to a file.\n\n    Args:\n        input_str: A string encoding the file system structure using\n            newlines and tabs.\n\n    Returns:\n        Length of the longest absolute path to a file, or 0 if no file exists.\n\n    Examples:\n        >>> length_longest_path(\"dir\\\\n\\\\tfile.txt\")\n        12\n    \"\"\"\n    current_length = 0\n    max_length = 0\n    stack: list[int] = []\n    for segment in input_str.split(\"\\n\"):\n        depth = segment.count(\"\\t\")\n        while len(stack) > depth:\n            current_length -= stack.pop()\n        name_length = len(segment.strip(\"\\t\")) + 1\n        stack.append(name_length)\n        current_length += stack[-1]\n        if \".\" in segment:\n            max_length = max(max_length, current_length - 1)\n    return max_length\n"
  },
  {
    "path": "algorithms/stack/ordered_stack.py",
    "content": "\"\"\"\nOrdered Stack\n\nA stack that maintains elements in sorted order, with the highest value\nat the top and the lowest at the bottom. Push operations preserve the\nordering invariant.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n) for push, O(1) for pop/peek\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass OrderedStack:\n    \"\"\"A stack that keeps elements in ascending order (bottom to top).\n\n    Examples:\n        >>> s = OrderedStack()\n        >>> s.push(3)\n        >>> s.push(1)\n        >>> s.push(2)\n        >>> s.pop()\n        3\n    \"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize an empty ordered stack.\"\"\"\n        self.items: list[int] = []\n\n    def is_empty(self) -> bool:\n        \"\"\"Check if the stack is empty.\n\n        Returns:\n            True if the stack has no elements.\n        \"\"\"\n        return self.items == []\n\n    def _push_direct(self, item: int) -> None:\n        \"\"\"Append an item without enforcing order.\n\n        Args:\n            item: The value to append.\n        \"\"\"\n        self.items.append(item)\n\n    def push(self, item: int) -> None:\n        \"\"\"Push an item while maintaining sorted order.\n\n        Args:\n            item: The value to push.\n        \"\"\"\n        temp_stack = OrderedStack()\n        if self.is_empty() or item > self.peek():\n            self._push_direct(item)\n        else:\n            while item < self.peek() and not self.is_empty():\n                temp_stack._push_direct(self.pop())\n            self._push_direct(item)\n            while not temp_stack.is_empty():\n                self._push_direct(temp_stack.pop())\n\n    def pop(self) -> int:\n        \"\"\"Remove and return the top element.\n\n        Returns:\n            The top (largest) element.\n\n        Raises:\n            IndexError: If the stack is empty.\n        \"\"\"\n        if self.is_empty():\n            raise IndexError(\"Stack is empty\")\n        return self.items.pop()\n\n    def peek(self) -> int:\n        \"\"\"Return the top element without removing it.\n\n        Returns:\n            The top (largest) element.\n        \"\"\"\n        return self.items[len(self.items) - 1]\n\n    def size(self) -> int:\n        \"\"\"Return the number of elements in the stack.\n\n        Returns:\n            The stack size.\n        \"\"\"\n        return len(self.items)\n"
  },
  {
    "path": "algorithms/stack/remove_min.py",
    "content": "\"\"\"\nRemove Min from Stack\n\nRemove the smallest value from a stack, preserving the relative order\nof the remaining elements.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef remove_min(stack: list[int]) -> list[int]:\n    \"\"\"Remove the minimum value from the stack.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        The stack with the minimum value removed.\n\n    Examples:\n        >>> remove_min([2, 8, 3, -6, 7, 3])\n        [2, 8, 3, 7, 3]\n    \"\"\"\n    storage_stack: list[int] = []\n    if len(stack) == 0:\n        return stack\n    minimum = stack.pop()\n    stack.append(minimum)\n    for _ in range(len(stack)):\n        val = stack.pop()\n        if val <= minimum:\n            minimum = val\n        storage_stack.append(val)\n    for _ in range(len(storage_stack)):\n        val = storage_stack.pop()\n        if val != minimum:\n            stack.append(val)\n    return stack\n"
  },
  {
    "path": "algorithms/stack/simplify_path.py",
    "content": "\"\"\"\nSimplify Path\n\nGiven an absolute Unix-style file path, simplify it by resolving '.'\n(current directory), '..' (parent directory), and multiple slashes.\n\nReference: https://leetcode.com/problems/simplify-path/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef simplify_path(path: str) -> str:\n    \"\"\"Simplify a Unix-style absolute path.\n\n    Args:\n        path: An absolute file path string.\n\n    Returns:\n        The simplified canonical path.\n\n    Examples:\n        >>> simplify_path(\"/home/\")\n        '/home'\n        >>> simplify_path(\"/a/./b/../../c/\")\n        '/c'\n    \"\"\"\n    skip = {\"..\", \".\", \"\"}\n    stack: list[str] = []\n    tokens = path.split(\"/\")\n    for token in tokens:\n        if token == \"..\":\n            if stack:\n                stack.pop()\n        elif token not in skip:\n            stack.append(token)\n    return \"/\" + \"/\".join(stack)\n"
  },
  {
    "path": "algorithms/stack/stutter.py",
    "content": "\"\"\"\nStutter\n\nReplace every value in a stack with two occurrences of that value.\nTwo approaches: one using an auxiliary stack, one using an auxiliary queue.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef first_stutter(stack: list[int]) -> list[int]:\n    \"\"\"Stutter a stack using an auxiliary stack.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        The stack with each value duplicated.\n\n    Examples:\n        >>> first_stutter([3, 7, 1, 14, 9])\n        [3, 3, 7, 7, 1, 1, 14, 14, 9, 9]\n    \"\"\"\n    storage_stack: list[int] = []\n    for _ in range(len(stack)):\n        storage_stack.append(stack.pop())\n    for _ in range(len(storage_stack)):\n        val = storage_stack.pop()\n        stack.append(val)\n        stack.append(val)\n\n    return stack\n\n\ndef second_stutter(stack: list[int]) -> list[int]:\n    \"\"\"Stutter a stack using an auxiliary queue.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        The stack with each value duplicated.\n\n    Examples:\n        >>> second_stutter([3, 7, 1, 14, 9])\n        [3, 3, 7, 7, 1, 1, 14, 14, 9, 9]\n    \"\"\"\n    queue: collections.deque[int] = collections.deque()\n    for _ in range(len(stack)):\n        queue.append(stack.pop())\n    for _ in range(len(queue)):\n        stack.append(queue.pop())\n    for _ in range(len(stack)):\n        queue.append(stack.pop())\n    for _ in range(len(queue)):\n        val = queue.pop()\n        stack.append(val)\n        stack.append(val)\n\n    return stack\n"
  },
  {
    "path": "algorithms/stack/switch_pairs.py",
    "content": "\"\"\"\nSwitch Pairs\n\nSwitch successive pairs of values in a stack starting from the bottom.\nIf there is an odd number of values, the top element is not moved.\nTwo approaches: one using an auxiliary stack, one using an auxiliary queue.\n\nReference: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef first_switch_pairs(stack: list[int]) -> list[int]:\n    \"\"\"Switch successive pairs using an auxiliary stack.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        The stack with successive pairs swapped.\n\n    Examples:\n        >>> first_switch_pairs([3, 8, 17, 9, 1, 10])\n        [8, 3, 9, 17, 10, 1]\n    \"\"\"\n    storage_stack: list[int] = []\n    for _ in range(len(stack)):\n        storage_stack.append(stack.pop())\n    for _ in range(len(storage_stack)):\n        if len(storage_stack) == 0:\n            break\n        first = storage_stack.pop()\n        if len(storage_stack) == 0:\n            stack.append(first)\n            break\n        second = storage_stack.pop()\n        stack.append(second)\n        stack.append(first)\n    return stack\n\n\ndef second_switch_pairs(stack: list[int]) -> list[int]:\n    \"\"\"Switch successive pairs using an auxiliary queue.\n\n    Args:\n        stack: A list representing a stack (bottom to top).\n\n    Returns:\n        The stack with successive pairs swapped.\n\n    Examples:\n        >>> second_switch_pairs([3, 8, 17, 9, 1, 10])\n        [8, 3, 9, 17, 10, 1]\n    \"\"\"\n    queue: collections.deque[int] = collections.deque()\n    for _ in range(len(stack)):\n        queue.append(stack.pop())\n    for _ in range(len(queue)):\n        stack.append(queue.pop())\n    for _ in range(len(stack)):\n        queue.append(stack.pop())\n    for _ in range(len(queue)):\n        if len(queue) == 0:\n            break\n        first = queue.pop()\n        if len(queue) == 0:\n            stack.append(first)\n            break\n        second = queue.pop()\n        stack.append(second)\n        stack.append(first)\n\n    return stack\n"
  },
  {
    "path": "algorithms/stack/valid_parenthesis.py",
    "content": "\"\"\"\nValid Parentheses\n\nDetermine if a string containing only '(', ')', '{', '}', '[' and ']'\nhas valid (properly closed and nested) brackets.\n\nReference: https://leetcode.com/problems/valid-parentheses/\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_valid(s: str) -> bool:\n    \"\"\"Check if the bracket string is valid.\n\n    Args:\n        s: A string containing only bracket characters.\n\n    Returns:\n        True if the brackets are valid, False otherwise.\n\n    Examples:\n        >>> is_valid(\"()[]{}\")\n        True\n        >>> is_valid(\"(]\")\n        False\n    \"\"\"\n    stack: list[str] = []\n    matching = {\")\": \"(\", \"}\": \"{\", \"]\": \"[\"}\n    for char in s:\n        if char in matching.values():\n            stack.append(char)\n        elif char in matching and (not stack or matching[char] != stack.pop()):\n                return False\n    return not stack\n"
  },
  {
    "path": "algorithms/streaming/__init__.py",
    "content": "from .misra_gries import misras_gries\nfrom .one_sparse_recovery import one_sparse\n\n__all__ = [\n    \"misras_gries\",\n    \"one_sparse\",\n]\n"
  },
  {
    "path": "algorithms/streaming/misra_gries.py",
    "content": "\"\"\"\nMisra-Gries Frequency Estimation\n\nGiven a list of items and a value k, returns every item that appears at least\nn/k times, where n is the length of the list. Defaults to k=2 (majority\nproblem).\n\nReference: https://en.wikipedia.org/wiki/Misra%E2%80%93Gries_summary\n\nComplexity:\n    Time:  O(n * k)\n    Space: O(k)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef misras_gries(array: list[int], k: int = 2) -> dict[str, int] | None:\n    \"\"\"Find all elements appearing at least n/k times.\n\n    Args:\n        array: A list of integers.\n        k: The frequency threshold divisor (default 2).\n\n    Returns:\n        A dict mapping element (as string) to its frequency, or None\n        if no element meets the threshold.\n\n    Examples:\n        >>> misras_gries([1, 4, 4, 4, 5, 4, 4])\n        {'4': 5}\n        >>> misras_gries([0, 0, 0, 1, 1, 1, 1])\n        {'1': 4}\n        >>> misras_gries([0, 0, 0, 0, 1, 1, 1, 2, 2], 3)\n        {'0': 4, '1': 3}\n    \"\"\"\n    keys: dict[str, int] = {}\n    for item in array:\n        val = str(item)\n        if val in keys:\n            keys[val] += 1\n        elif len(keys) < k - 1:\n            keys[val] = 1\n        else:\n            for key in list(keys):\n                keys[key] -= 1\n                if keys[key] == 0:\n                    del keys[key]\n\n    suspects = keys.keys()\n    frequencies: dict[str, int] = {}\n    for suspect in suspects:\n        freq = _count_frequency(array, int(suspect))\n        if freq >= len(array) / k:\n            frequencies[suspect] = freq\n\n    return frequencies if frequencies else None\n\n\ndef _count_frequency(array: list[int], element: int) -> int:\n    \"\"\"Count occurrences of element in array.\n\n    Args:\n        array: The list to search.\n        element: The value to count.\n\n    Returns:\n        The number of occurrences.\n    \"\"\"\n    return array.count(element)\n"
  },
  {
    "path": "algorithms/streaming/one_sparse_recovery.py",
    "content": "\"\"\"\nNon-negative 1-Sparse Recovery\n\nDetermines whether a dynamic stream of (value, sign) tuples is 1-sparse,\nmeaning all values cancel out except for a single unique number. If so,\nreturns that number; otherwise returns None.\n\nReference: https://en.wikipedia.org/wiki/Sparse_recovery\n\nComplexity:\n    Time:  O(n * b) where n is stream length and b is bit width (32)\n    Space: O(b)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef one_sparse(array: list[tuple[int, str]]) -> int | None:\n    \"\"\"Recover the unique element from a 1-sparse stream.\n\n    Args:\n        array: A list of (value, sign) tuples where sign is '+' or '-'.\n\n    Returns:\n        The unique element if the stream is 1-sparse, otherwise None.\n\n    Examples:\n        >>> one_sparse([(4, '+'), (2, '+'), (2, '-'), (4, '+'), (3, '+'), (3, '-')])\n        4\n        >>> one_sparse([(2, '+'), (2, '+'), (2, '+'),\n        ...            (2, '+'), (2, '+'), (2, '+'), (1, '+')])\n    \"\"\"\n    sum_signs = 0\n    bitsum: list[int] = [0] * 32\n    sum_values = 0\n\n    for val, sign in array:\n        if sign == \"+\":\n            sum_signs += 1\n            sum_values += val\n        else:\n            sum_signs -= 1\n            sum_values -= val\n        _update_bit_sum(bitsum, val, sign)\n\n    if sum_signs > 0 and _check_bit_sum_consistency(bitsum, sum_signs):\n        return int(sum_values / sum_signs)\n    return None\n\n\ndef _check_bit_sum_consistency(bitsum: list[int], sum_signs: int) -> bool:\n    \"\"\"Check that every entry is either 0 or equal to sum_signs.\n\n    Args:\n        bitsum: The accumulated bit sums.\n        sum_signs: The expected non-zero value.\n\n    Returns:\n        True if the bitsum is consistent with a 1-sparse stream.\n    \"\"\"\n    return all(val == 0 or val == sum_signs for val in bitsum)\n\n\ndef _update_bit_sum(bitsum: list[int], val: int, sign: str) -> None:\n    \"\"\"Add or subtract the bit representation of val to the bitsum array.\n\n    Args:\n        bitsum: The accumulated bit sums to update in place.\n        val: The integer value whose bits to process.\n        sign: '+' to add or '-' to subtract.\n    \"\"\"\n    idx = 0\n    if sign == \"+\":\n        while val:\n            bitsum[idx] += val & 1\n            idx += 1\n            val >>= 1\n    else:\n        while val:\n            bitsum[idx] -= val & 1\n            idx += 1\n            val >>= 1\n"
  },
  {
    "path": "algorithms/string/__init__.py",
    "content": "\"\"\"String algorithms package.\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.string import fizzbuzz\nfrom algorithms.string.add_binary import add_binary\nfrom algorithms.string.alphabet_board_path import alphabet_board_path\nfrom algorithms.string.atbash_cipher import atbash\nfrom algorithms.string.breaking_bad import bracket, match_symbol, match_symbol_1\nfrom algorithms.string.caesar_cipher import caesar_cipher\nfrom algorithms.string.check_pangram import check_pangram\nfrom algorithms.string.contain_string import contain_string\nfrom algorithms.string.count_binary_substring import count_binary_substring\nfrom algorithms.string.decode_string import decode_string\nfrom algorithms.string.delete_reoccurring import delete_reoccurring_characters\nfrom algorithms.string.domain_extractor import domain_name_1, domain_name_2\nfrom algorithms.string.encode_decode import decode, encode\nfrom algorithms.string.first_unique_char import first_unique_char\nfrom algorithms.string.fizzbuzz import fizzbuzz_with_helper_func\nfrom algorithms.string.group_anagrams import group_anagrams\nfrom algorithms.string.int_to_roman import int_to_roman\nfrom algorithms.string.is_palindrome import (\n    is_palindrome,\n    is_palindrome_deque,\n    is_palindrome_reverse,\n    is_palindrome_stack,\n    is_palindrome_two_pointer,\n)\nfrom algorithms.string.is_rotated import is_rotated, is_rotated_v1\nfrom algorithms.string.judge_circle import judge_circle\nfrom algorithms.string.knuth_morris_pratt import knuth_morris_pratt\nfrom algorithms.string.license_number import license_number\nfrom algorithms.string.longest_common_prefix import (\n    longest_common_prefix_v1,\n    longest_common_prefix_v2,\n    longest_common_prefix_v3,\n)\nfrom algorithms.string.longest_palindromic_substring import longest_palindrome\nfrom algorithms.string.make_sentence import make_sentence\nfrom algorithms.string.manacher import manacher\nfrom algorithms.string.merge_string_checker import (\n    is_merge_iterative,\n    is_merge_recursive,\n)\nfrom algorithms.string.min_distance import min_distance, min_distance_dp\nfrom algorithms.string.multiply_strings import multiply\nfrom algorithms.string.one_edit_distance import is_one_edit, is_one_edit2\nfrom algorithms.string.panagram import panagram\nfrom algorithms.string.rabin_karp import RollingHash, rabin_karp\nfrom algorithms.string.repeat_string import repeat_string\nfrom algorithms.string.repeat_substring import repeat_substring\nfrom algorithms.string.reverse_string import (\n    iterative,\n    pythonic,\n    recursive,\n    ultra_pythonic,\n)\nfrom algorithms.string.reverse_vowel import reverse_vowel\nfrom algorithms.string.reverse_words import reverse_words\nfrom algorithms.string.roman_to_int import roman_to_int\nfrom algorithms.string.rotate import rotate, rotate_alt\nfrom algorithms.string.strip_url_params import (\n    strip_url_params1,\n    strip_url_params2,\n    strip_url_params3,\n)\nfrom algorithms.string.strong_password import strong_password\nfrom algorithms.string.swap_characters import can_swap_to_equal\nfrom algorithms.string.text_justification import text_justification\nfrom algorithms.string.unique_morse import convert_morse_word, unique_morse\nfrom algorithms.string.validate_coordinates import (\n    is_valid_coordinates_0,\n    is_valid_coordinates_1,\n    is_valid_coordinates_regular_expression,\n)\nfrom algorithms.string.word_squares import word_squares\nfrom algorithms.string.z_algorithm import compute_z_array, z_search\n\n__all__ = [\n    \"add_binary\",\n    \"atbash\",\n    \"bracket\",\n    \"caesar_cipher\",\n    \"check_pangram\",\n    \"contain_string\",\n    \"convert_morse_word\",\n    \"count_binary_substring\",\n    \"decode\",\n    \"decode_string\",\n    \"delete_reoccurring_characters\",\n    \"domain_name_1\",\n    \"domain_name_2\",\n    \"encode\",\n    \"first_unique_char\",\n    \"fizzbuzz\",\n    \"fizzbuzz_with_helper_func\",\n    \"group_anagrams\",\n    \"int_to_roman\",\n    \"is_merge_iterative\",\n    \"is_merge_recursive\",\n    \"is_one_edit\",\n    \"is_one_edit2\",\n    \"is_palindrome\",\n    \"is_palindrome_deque\",\n    \"is_palindrome_reverse\",\n    \"is_palindrome_stack\",\n    \"is_palindrome_two_pointer\",\n    \"is_rotated\",\n    \"is_rotated_v1\",\n    \"is_valid_coordinates_0\",\n    \"is_valid_coordinates_1\",\n    \"is_valid_coordinates_regular_expression\",\n    \"iterative\",\n    \"judge_circle\",\n    \"knuth_morris_pratt\",\n    \"license_number\",\n    \"longest_common_prefix_v1\",\n    \"longest_common_prefix_v2\",\n    \"longest_common_prefix_v3\",\n    \"longest_palindrome\",\n    \"make_sentence\",\n    \"match_symbol\",\n    \"match_symbol_1\",\n    \"min_distance\",\n    \"min_distance_dp\",\n    \"multiply\",\n    \"panagram\",\n    \"pythonic\",\n    \"rabin_karp\",\n    \"recursive\",\n    \"repeat_string\",\n    \"repeat_substring\",\n    \"reverse_vowel\",\n    \"reverse_words\",\n    \"RollingHash\",\n    \"roman_to_int\",\n    \"rotate\",\n    \"rotate_alt\",\n    \"strong_password\",\n    \"strip_url_params1\",\n    \"strip_url_params2\",\n    \"strip_url_params3\",\n    \"text_justification\",\n    \"ultra_pythonic\",\n    \"unique_morse\",\n    \"word_squares\",\n    \"z_search\",\n    \"compute_z_array\",\n    \"alphabet_board_path\",\n    \"manacher\",\n    \"can_swap_to_equal\",\n]\n"
  },
  {
    "path": "algorithms/string/add_binary.py",
    "content": "\"\"\"\nAdd Binary\n\nGiven two binary strings, return their sum as a binary string.\n\nReference: https://leetcode.com/problems/add-binary/\n\nComplexity:\n    Time:  O(max(m, n)) where m, n are lengths of the two strings\n    Space: O(max(m, n))\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef add_binary(first: str, second: str) -> str:\n    \"\"\"Add two binary strings and return their binary sum.\n\n    Args:\n        first: A string representing a binary number.\n        second: A string representing a binary number.\n\n    Returns:\n        A string representing the binary sum of the two inputs.\n\n    Examples:\n        >>> add_binary(\"11\", \"1\")\n        '100'\n    \"\"\"\n    result = \"\"\n    carry, index_a, index_b = 0, len(first) - 1, len(second) - 1\n    zero = ord(\"0\")\n    while index_a >= 0 or index_b >= 0 or carry == 1:\n        if index_a >= 0:\n            carry += ord(first[index_a]) - zero\n            index_a -= 1\n        if index_b >= 0:\n            carry += ord(second[index_b]) - zero\n            index_b -= 1\n        result = chr(carry % 2 + zero) + result\n        carry //= 2\n    return result\n"
  },
  {
    "path": "algorithms/string/alphabet_board_path.py",
    "content": "\"\"\"Alphabet board path — find moves on a 5x5+1 letter board.\n\nGiven a board where 'a'-'z' are laid out in rows of 5:\n    a b c d e\n    f g h i j\n    k l m n o\n    p q r s t\n    u v w x y\n    z\n\nReturn the sequence of moves (U/D/L/R/!) to spell a target word\nstarting from 'a'.\n\nInspired by PR #897 (chnttx).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef alphabet_board_path(target: str) -> str:\n    \"\"\"Return move string to spell *target* on the alphabet board.\"\"\"\n    moves: list[str] = []\n    row, col = 0, 0\n    for ch in target:\n        idx = ord(ch) - ord(\"a\")\n        target_row, target_col = divmod(idx, 5)\n        # Move up/left before down/right to avoid going off-board at 'z'\n        if target_row < row:\n            moves.append(\"U\" * (row - target_row))\n        if target_col < col:\n            moves.append(\"L\" * (col - target_col))\n        if target_row > row:\n            moves.append(\"D\" * (target_row - row))\n        if target_col > col:\n            moves.append(\"R\" * (target_col - col))\n        moves.append(\"!\")\n        row, col = target_row, target_col\n    return \"\".join(moves)\n"
  },
  {
    "path": "algorithms/string/atbash_cipher.py",
    "content": "\"\"\"\nAtbash Cipher\n\nAtbash cipher maps each letter of the alphabet to its reverse.\nThe first letter 'a' maps to 'z', 'b' maps to 'y', and so on.\n\nReference: https://en.wikipedia.org/wiki/Atbash\n\nComplexity:\n    Time:  O(n) where n is the length of the input string\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef atbash(text: str) -> str:\n    \"\"\"Encrypt or decrypt a string using the Atbash cipher.\n\n    Args:\n        text: The input string to transform.\n\n    Returns:\n        The Atbash-transformed string.\n\n    Examples:\n        >>> atbash(\"abcdefghijklmno\")\n        'zyxwvutsrqponml'\n    \"\"\"\n    translated = \"\"\n    for char in text:\n        code = ord(char)\n        if char.isalpha():\n            if char.isupper():\n                offset = code - ord(\"A\")\n                translated += chr(ord(\"Z\") - offset)\n            elif char.islower():\n                offset = code - ord(\"a\")\n                translated += chr(ord(\"z\") - offset)\n        else:\n            translated += char\n    return translated\n"
  },
  {
    "path": "algorithms/string/breaking_bad.py",
    "content": "\"\"\"\nBreaking Bad Symbol Matching\n\nGiven an array of words and an array of symbols, display each word with its\nmatched symbol surrounded by square brackets. If a word matches more than one\nsymbol, choose the one with the longest length.\n\nReference: https://en.wikipedia.org/wiki/Trie\n\nComplexity:\n    Time:  O(n * m) for brute force, O(n * k) for trie-based approach\n    Space: O(n * m) for storing results\n\"\"\"\n\nfrom __future__ import annotations\n\nimport re\nfrom functools import reduce\n\n\ndef match_symbol(words: list[str], symbols: list[str]) -> list[str]:\n    \"\"\"Match symbols in words using regex and surround matches with brackets.\n\n    Args:\n        words: List of words to search through.\n        symbols: List of symbols to match within the words.\n\n    Returns:\n        List of words with matched symbols surrounded by square brackets.\n\n    Examples:\n        >>> match_symbol(['Google'], ['le'])\n        ['Goog[le]']\n    \"\"\"\n    combined = []\n    for symbol in symbols:\n        for word in words:\n            match = re.search(symbol, word)\n            if match:\n                combined.append(re.sub(symbol, f\"[{symbol}]\", word))\n    return combined\n\n\ndef match_symbol_1(words: list[str], symbols: list[str]) -> list[str]:\n    \"\"\"Match the longest symbol in each word using sorted symbol list.\n\n    Args:\n        words: List of words to search through.\n        symbols: List of symbols to match, sorted by length descending.\n\n    Returns:\n        List of words with the longest matched symbol bracketed.\n\n    Examples:\n        >>> match_symbol_1(['Microsoft'], ['i', 'cro'])\n        ['Mi[cro]soft']\n    \"\"\"\n    result = []\n    symbols = sorted(symbols, key=lambda item: len(item), reverse=True)\n    for word in words:\n        word_replaced = \"\"\n        for symbol in symbols:\n            if word.find(symbol) != -1:\n                word_replaced = word.replace(symbol, \"[\" + symbol + \"]\")\n                result.append(word_replaced)\n                break\n        if word_replaced == \"\":\n            result.append(word)\n    return result\n\n\nclass _TrieNode:\n    \"\"\"Internal trie node for the bracket function.\"\"\"\n\n    def __init__(self) -> None:\n        self.children: dict[str, _TrieNode] = {}\n        self.symbol: str | None = None\n\n\ndef bracket(words: list[str], symbols: list[str]) -> tuple[str, ...]:\n    \"\"\"Match the longest symbol in each word using a trie-based approach.\n\n    Args:\n        words: List of words to search through.\n        symbols: List of symbols to build the trie from.\n\n    Returns:\n        Tuple of words with the longest matched symbol bracketed.\n\n    Examples:\n        >>> bracket(['Amazon', 'Microsoft', 'Google'], ['Am', 'cro', 'le'])\n        ('[Am]azon', 'Mi[cro]soft', 'Goog[le]')\n    \"\"\"\n    root = _TrieNode()\n    for symbol in symbols:\n        node = root\n        for char in symbol:\n            if char not in node.children:\n                node.children[char] = _TrieNode()\n            node = node.children[char]\n        node.symbol = symbol\n\n    matched = {}\n    for word in words:\n        index = 0\n        symbol_list = []\n        while index < len(word):\n            cursor, node = index, root\n            while cursor < len(word) and word[cursor] in node.children:\n                node = node.children[word[cursor]]\n                if node.symbol is not None:\n                    symbol_list.append(\n                        (cursor + 1 - len(node.symbol), cursor + 1, node.symbol)\n                    )\n                cursor += 1\n            index += 1\n        if len(symbol_list) > 0:\n            best = reduce(\n                lambda x, y: x if x[1] - x[0] >= y[1] - y[0] else y,\n                symbol_list,\n            )\n            matched[word] = f\"{word[: best[0]]}[{best[2]}]{word[best[1] :]}\"\n\n    return tuple(matched.get(word, word) for word in words)\n"
  },
  {
    "path": "algorithms/string/caesar_cipher.py",
    "content": "\"\"\"\nCaesar Cipher\n\nCaesar's cipher shifts each letter by a fixed number of positions in the\nalphabet. Letters wrap around when they pass the end of the alphabet.\n\nReference: https://en.wikipedia.org/wiki/Caesar_cipher\n\nComplexity:\n    Time:  O(n) where n is the length of the input string\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef caesar_cipher(text: str, shift: int) -> str:\n    \"\"\"Encrypt a string using the Caesar cipher with the given shift.\n\n    Args:\n        text: The plaintext string to encrypt.\n        shift: The number of positions to shift each letter.\n\n    Returns:\n        The encrypted string with shifted letters.\n\n    Examples:\n        >>> caesar_cipher(\"Hello_World!\", 4)\n        'Lipps_Asvph!'\n    \"\"\"\n    result = \"\"\n    for char in text:\n        code = ord(char)\n        if 64 < code < 91:\n            code = ((code - 65 + shift) % 26) + 65\n        if 96 < code < 123:\n            code = ((code - 97 + shift) % 26) + 97\n        result = result + chr(code)\n    return result\n"
  },
  {
    "path": "algorithms/string/check_pangram.py",
    "content": "\"\"\"\nCheck Pangram\n\nChecks whether a given string is a pangram, meaning it contains every\nletter of the English alphabet at least once.\n\nReference: https://en.wikipedia.org/wiki/Pangram\n\nComplexity:\n    Time:  O(n) where n is the length of the input string\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef check_pangram(input_string: str) -> bool:\n    \"\"\"Check if the input string is a pangram.\n\n    Args:\n        input_string: The string to check.\n\n    Returns:\n        True if the string contains every letter of the alphabet, False otherwise.\n\n    Examples:\n        >>> check_pangram(\"The quick brown fox jumps over the lazy dog\")\n        True\n    \"\"\"\n    alphabet = \"abcdefghijklmnopqrstuvwxyz\"\n    return all(char in input_string.lower() for char in alphabet)\n"
  },
  {
    "path": "algorithms/string/contain_string.py",
    "content": "\"\"\"\nContain String (strStr)\n\nReturn the index of the first occurrence of needle in haystack, or -1 if\nneedle is not part of haystack.\n\nReference: https://leetcode.com/problems/implement-strstr/\n\nComplexity:\n    Time:  O(n * m) worst case, where n = len(haystack), m = len(needle)\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef contain_string(haystack: str, needle: str) -> int:\n    \"\"\"Find the first occurrence of needle in haystack.\n\n    Args:\n        haystack: The string to search in.\n        needle: The string to search for.\n\n    Returns:\n        The index of the first occurrence, or -1 if not found.\n\n    Examples:\n        >>> contain_string(\"hello\", \"ll\")\n        2\n    \"\"\"\n    if len(needle) == 0:\n        return 0\n    if len(needle) > len(haystack):\n        return -1\n    for index in range(len(haystack)):\n        if len(haystack) - index < len(needle):\n            return -1\n        if haystack[index : index + len(needle)] == needle:\n            return index\n    return -1\n"
  },
  {
    "path": "algorithms/string/count_binary_substring.py",
    "content": "\"\"\"\nCount Binary Substrings\n\nCount the number of non-empty contiguous substrings that have the same number\nof 0s and 1s, where all 0s and all 1s are grouped consecutively.\n\nReference: https://leetcode.com/problems/count-binary-substrings/\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef count_binary_substring(text: str) -> int:\n    \"\"\"Count substrings with equal consecutive 0s and 1s.\n\n    Args:\n        text: A binary string consisting of '0' and '1' characters.\n\n    Returns:\n        The number of valid binary substrings.\n\n    Examples:\n        >>> count_binary_substring(\"00110011\")\n        6\n    \"\"\"\n    current = 1\n    previous = 0\n    count = 0\n    for index in range(1, len(text)):\n        if text[index] != text[index - 1]:\n            count = count + min(previous, current)\n            previous = current\n            current = 1\n        else:\n            current = current + 1\n    count = count + min(previous, current)\n    return count\n"
  },
  {
    "path": "algorithms/string/decode_string.py",
    "content": "\"\"\"\nDecode String\n\nGiven an encoded string, return its decoded string. The encoding rule is\nk[encoded_string], where the encoded_string inside the brackets is repeated\nexactly k times.\n\nReference: https://leetcode.com/problems/decode-string/\n\nComplexity:\n    Time:  O(n * max_k) where n is the length of the string\n    Space: O(n) for the stack\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef decode_string(text: str) -> str:\n    \"\"\"Decode an encoded string with nested repeat patterns.\n\n    Args:\n        text: The encoded string in the format k[encoded_string].\n\n    Returns:\n        The fully decoded string.\n\n    Examples:\n        >>> decode_string(\"3[a]2[bc]\")\n        'aaabcbc'\n    \"\"\"\n    stack: list[tuple[str, int]] = []\n    current_num = 0\n    current_string = \"\"\n    for char in text:\n        if char == \"[\":\n            stack.append((current_string, current_num))\n            current_string = \"\"\n            current_num = 0\n        elif char == \"]\":\n            prev_string, num = stack.pop()\n            current_string = prev_string + num * current_string\n        elif char.isdigit():\n            current_num = current_num * 10 + int(char)\n        else:\n            current_string += char\n    return current_string\n"
  },
  {
    "path": "algorithms/string/delete_reoccurring.py",
    "content": "\"\"\"\nDelete Reoccurring Characters\n\nGiven a string, delete any reoccurring characters and return the new string\ncontaining only the first occurrence of each character.\n\nReference: https://en.wikipedia.org/wiki/Duplicate_removal\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef delete_reoccurring_characters(string: str) -> str:\n    \"\"\"Remove duplicate characters, keeping only the first occurrence of each.\n\n    Args:\n        string: The input string to process.\n\n    Returns:\n        A new string with duplicate characters removed.\n\n    Examples:\n        >>> delete_reoccurring_characters(\"aaabcccc\")\n        'abc'\n    \"\"\"\n    seen_characters: set[str] = set()\n    output_string = \"\"\n    for char in string:\n        if char not in seen_characters:\n            seen_characters.add(char)\n            output_string += char\n    return output_string\n"
  },
  {
    "path": "algorithms/string/domain_extractor.py",
    "content": "\"\"\"\nDomain Name Extractor\n\nGiven a URL as a string, parse out just the domain name and return it.\nUses only the .split() built-in function without regex or urlparse.\n\nReference: https://en.wikipedia.org/wiki/Domain_name\n\nComplexity:\n    Time:  O(n) where n is the length of the URL\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef domain_name_1(url: str) -> str:\n    \"\"\"Extract the domain name from a URL by splitting on protocol and dots.\n\n    Args:\n        url: The full URL string.\n\n    Returns:\n        The domain name extracted from the URL.\n\n    Examples:\n        >>> domain_name_1(\"https://github.com/SaadBenn\")\n        'github'\n    \"\"\"\n    full_domain_name = url.split(\"//\")[-1]\n    actual_domain = full_domain_name.split(\".\")\n    if len(actual_domain) > 2:\n        return actual_domain[1]\n    return actual_domain[0]\n\n\ndef domain_name_2(url: str) -> str:\n    \"\"\"Extract the domain name from a URL using chained splits.\n\n    Args:\n        url: The full URL string.\n\n    Returns:\n        The domain name extracted from the URL.\n\n    Examples:\n        >>> domain_name_2(\"http://google.com\")\n        'google'\n    \"\"\"\n    return url.split(\"//\")[-1].split(\"www.\")[-1].split(\".\")[0]\n"
  },
  {
    "path": "algorithms/string/encode_decode.py",
    "content": "\"\"\"\nEncode and Decode Strings\n\nDesign an algorithm to encode a list of strings to a single string, and\ndecode it back to the original list of strings.\n\nReference: https://leetcode.com/problems/encode-and-decode-strings/\n\nComplexity:\n    Time:  O(n) for both encode and decode\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef encode(strs: str) -> str:\n    \"\"\"Encode a space-separated string into a length-prefixed format.\n\n    Args:\n        strs: A space-separated string of words.\n\n    Returns:\n        A single encoded string with length-prefixed words.\n\n    Examples:\n        >>> encode(\"keon is awesome\")\n        '4:keon2:is7:awesome'\n    \"\"\"\n    result = \"\"\n    for word in strs.split():\n        result += str(len(word)) + \":\" + word\n    return result\n\n\ndef decode(text: str) -> list[str]:\n    \"\"\"Decode a length-prefixed string back into a list of strings.\n\n    Args:\n        text: The encoded string with length-prefixed words.\n\n    Returns:\n        A list of the original decoded strings.\n\n    Examples:\n        >>> decode(\"4:keon2:is7:awesome\")\n        ['keon', 'is', 'awesome']\n    \"\"\"\n    words: list[str] = []\n    index = 0\n    while index < len(text):\n        colon_index = text.find(\":\", index)\n        size = int(text[index:colon_index])\n        words.append(text[colon_index + 1 : colon_index + 1 + size])\n        index = colon_index + 1 + size\n    return words\n"
  },
  {
    "path": "algorithms/string/first_unique_char.py",
    "content": "\"\"\"\nFirst Unique Character in a String\n\nGiven a string, find the first non-repeating character and return its index.\nIf no unique character exists, return -1.\n\nReference: https://leetcode.com/problems/first-unique-character-in-a-string/\n\nComplexity:\n    Time:  O(n^2) worst case due to nested comparisons\n    Space: O(n) for the banned list\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef first_unique_char(text: str) -> int:\n    \"\"\"Find the index of the first non-repeating character in a string.\n\n    Args:\n        text: The input string to search.\n\n    Returns:\n        The index of the first unique character, or -1 if none exists.\n\n    Examples:\n        >>> first_unique_char(\"leetcode\")\n        0\n    \"\"\"\n    if len(text) == 1:\n        return 0\n    banned: list[str] = []\n    for index in range(len(text)):\n        if (\n            all(text[index] != text[other] for other in range(index + 1, len(text)))\n            and text[index] not in banned\n        ):\n            return index\n        else:\n            banned.append(text[index])\n    return -1\n"
  },
  {
    "path": "algorithms/string/fizzbuzz.py",
    "content": "\"\"\"\nFizzBuzz\n\nReturn an array of numbers from 1 to N, replacing multiples of 3 with 'Fizz',\nmultiples of 5 with 'Buzz', and multiples of both with 'FizzBuzz'.\n\nReference: https://en.wikipedia.org/wiki/Fizz_buzz\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef fizzbuzz(number: int) -> list[int | str]:\n    \"\"\"Generate FizzBuzz sequence from 1 to number.\n\n    Args:\n        number: The upper bound of the sequence (inclusive).\n\n    Returns:\n        A list where multiples of 3 are 'Fizz', multiples of 5 are 'Buzz',\n        multiples of both are 'FizzBuzz', and all others are the integer value.\n\n    Raises:\n        ValueError: If number is less than 1.\n        TypeError: If number is None.\n\n    Examples:\n        >>> fizzbuzz(5)\n        [1, 2, 'Fizz', 4, 'Buzz']\n    \"\"\"\n    if number < 1:\n        raise ValueError(\"n cannot be less than one\")\n    if number is None:\n        raise TypeError(\"n cannot be None\")\n\n    result: list[int | str] = []\n    for value in range(1, number + 1):\n        if value % 3 == 0 and value % 5 == 0:\n            result.append(\"FizzBuzz\")\n        elif value % 3 == 0:\n            result.append(\"Fizz\")\n        elif value % 5 == 0:\n            result.append(\"Buzz\")\n        else:\n            result.append(value)\n    return result\n\n\ndef fizzbuzz_with_helper_func(number: int) -> list[int | str]:\n    \"\"\"Generate FizzBuzz sequence using a helper function.\n\n    Args:\n        number: The upper bound of the sequence (inclusive).\n\n    Returns:\n        A list of FizzBuzz values from 1 to number.\n\n    Examples:\n        >>> fizzbuzz_with_helper_func(3)\n        [1, 2, 'Fizz']\n    \"\"\"\n    return [_fb(value) for value in range(1, number + 1)]\n\n\ndef _fb(value: int) -> int | str:\n    \"\"\"Return the FizzBuzz value for a single number.\n\n    Args:\n        value: The number to evaluate.\n\n    Returns:\n        'Fizz', 'Buzz', 'FizzBuzz', or the number itself.\n    \"\"\"\n    result = (value % 3 == 0) * \"Fizz\" + (value % 5 == 0) * \"Buzz\"\n    return result if result != \"\" else value\n"
  },
  {
    "path": "algorithms/string/group_anagrams.py",
    "content": "\"\"\"\nGroup Anagrams\n\nGiven an array of strings, group anagrams together. Anagrams are words that\ncontain the same letters in a different order.\n\nReference: https://leetcode.com/problems/group-anagrams/\n\nComplexity:\n    Time:  O(n * k log k) where n is the number of strings and k is max length\n    Space: O(n * k)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef group_anagrams(strings: list[str]) -> list[list[str]]:\n    \"\"\"Group a list of strings by anagram equivalence.\n\n    Args:\n        strings: A list of strings to group.\n\n    Returns:\n        A list of groups, where each group contains strings that are anagrams.\n\n    Examples:\n        >>> group_anagrams([\"eat\", \"tea\", \"tan\", \"ate\", \"nat\", \"bat\"])\n        [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]\n    \"\"\"\n    anagram_map: dict[str, int] = {}\n    groups: list[list[str]] = []\n    group_index = 0\n    for word in strings:\n        sorted_word = \"\".join(sorted(word))\n        if sorted_word not in anagram_map:\n            anagram_map[sorted_word] = group_index\n            group_index += 1\n            groups.append([])\n            groups[-1].append(word)\n        else:\n            groups[anagram_map[sorted_word]].append(word)\n    return groups\n"
  },
  {
    "path": "algorithms/string/int_to_roman.py",
    "content": "\"\"\"\nInteger to Roman Numeral\n\nGiven an integer, convert it to a Roman numeral string. Input is guaranteed\nto be within the range from 1 to 3999.\n\nReference: https://en.wikipedia.org/wiki/Roman_numerals\n\nComplexity:\n    Time:  O(1) since the input range is bounded\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef int_to_roman(num: int) -> str:\n    \"\"\"Convert an integer to its Roman numeral representation.\n\n    Args:\n        num: An integer between 1 and 3999.\n\n    Returns:\n        The Roman numeral string for the given integer.\n\n    Examples:\n        >>> int_to_roman(644)\n        'DCXLIV'\n    \"\"\"\n    thousands = [\"\", \"M\", \"MM\", \"MMM\"]\n    hundreds = [\"\", \"C\", \"CC\", \"CCC\", \"CD\", \"D\", \"DC\", \"DCC\", \"DCCC\", \"CM\"]\n    tens = [\"\", \"X\", \"XX\", \"XXX\", \"XL\", \"L\", \"LX\", \"LXX\", \"LXXX\", \"XC\"]\n    ones = [\"\", \"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"VII\", \"VIII\", \"IX\"]\n    return (\n        thousands[num // 1000]\n        + hundreds[(num % 1000) // 100]\n        + tens[(num % 100) // 10]\n        + ones[num % 10]\n    )\n"
  },
  {
    "path": "algorithms/string/is_palindrome.py",
    "content": "\"\"\"\nIs Palindrome\n\nDetermine if a string is a palindrome, considering only alphanumeric\ncharacters and ignoring cases. Multiple approaches are provided.\n\nReference: https://en.wikipedia.org/wiki/Palindrome\n\nComplexity:\n    Time:  O(n) for all variations\n    Space: O(n) for variations that create new strings, O(1) for two-pointer\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\nfrom string import ascii_letters\n\n\ndef is_palindrome(text: str) -> bool:\n    \"\"\"Check if a string is a palindrome using two pointers on the original.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome(\"Otto\")\n        True\n    \"\"\"\n    left = 0\n    right = len(text) - 1\n    while left < right:\n        while not text[left].isalnum():\n            left += 1\n        while not text[right].isalnum():\n            right -= 1\n        if text[left].lower() != text[right].lower():\n            return False\n        left, right = left + 1, right - 1\n    return True\n\n\ndef _remove_punctuation(text: str) -> str:\n    \"\"\"Remove punctuation, case sensitivity and spaces from a string.\n\n    Args:\n        text: The input string to clean.\n\n    Returns:\n        A lowercase string with only alphabetic characters.\n    \"\"\"\n    return \"\".join(char.lower() for char in text if char in ascii_letters)\n\n\ndef _string_reverse(text: str) -> str:\n    \"\"\"Reverse a string using slicing.\n\n    Args:\n        text: The string to reverse.\n\n    Returns:\n        The reversed string.\n    \"\"\"\n    return text[::-1]\n\n\ndef is_palindrome_reverse(text: str) -> bool:\n    \"\"\"Check if a string is a palindrome by comparing with its reverse.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_reverse(\"Otto\")\n        True\n    \"\"\"\n    text = _remove_punctuation(text)\n    return text == _string_reverse(text)\n\n\ndef is_palindrome_two_pointer(text: str) -> bool:\n    \"\"\"Check if a string is a palindrome using two pointers from both ends.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_two_pointer(\"Otto\")\n        True\n    \"\"\"\n    text = _remove_punctuation(text)\n    for index in range(0, len(text) // 2):\n        if text[index] != text[len(text) - index - 1]:\n            return False\n    return True\n\n\ndef is_palindrome_stack(text: str) -> bool:\n    \"\"\"Check if a string is a palindrome using a stack.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_stack(\"Otto\")\n        True\n    \"\"\"\n    stack: list[str] = []\n    text = _remove_punctuation(text)\n    for index in range(len(text) // 2, len(text)):\n        stack.append(text[index])\n    return all(text[index] == stack.pop() for index in range(0, len(text) // 2))\n\n\ndef is_palindrome_deque(text: str) -> bool:\n    \"\"\"Check if a string is a palindrome using a deque.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a palindrome, False otherwise.\n\n    Examples:\n        >>> is_palindrome_deque(\"Otto\")\n        True\n    \"\"\"\n    text = _remove_punctuation(text)\n    character_deque: deque[str] = deque()\n    for char in text:\n        character_deque.appendleft(char)\n\n    equal = True\n    while len(character_deque) > 1 and equal:\n        first = character_deque.pop()\n        last = character_deque.popleft()\n        if first != last:\n            equal = False\n\n    return equal\n"
  },
  {
    "path": "algorithms/string/is_rotated.py",
    "content": "\"\"\"\nIs Rotated String\n\nGiven two strings, determine if the second is a rotated version of the first.\nTwo approaches are provided: concatenation check and brute force.\n\nReference: https://leetcode.com/problems/rotate-string/\n\nComplexity:\n    Time:  O(n) for concatenation approach, O(n^2) for brute force\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_rotated(first: str, second: str) -> bool:\n    \"\"\"Check if second is a rotation of first using string concatenation.\n\n    Args:\n        first: The original string.\n        second: The string to check as a rotation.\n\n    Returns:\n        True if second is a rotation of first, False otherwise.\n\n    Examples:\n        >>> is_rotated(\"hello\", \"llohe\")\n        True\n    \"\"\"\n    if len(first) == len(second):\n        return second in first + first\n    else:\n        return False\n\n\ndef is_rotated_v1(first: str, second: str) -> bool:\n    \"\"\"Check if second is a rotation of first using brute force comparison.\n\n    Args:\n        first: The original string.\n        second: The string to check as a rotation.\n\n    Returns:\n        True if second is a rotation of first, False otherwise.\n\n    Examples:\n        >>> is_rotated_v1(\"hello\", \"llohe\")\n        True\n    \"\"\"\n    if len(first) != len(second):\n        return False\n    if len(first) == 0:\n        return True\n\n    for offset in range(len(first)):\n        if all(\n            first[(offset + index) % len(first)] == second[index]\n            for index in range(len(first))\n        ):\n            return True\n    return False\n"
  },
  {
    "path": "algorithms/string/judge_circle.py",
    "content": "\"\"\"\nJudge Route Circle\n\nGiven a sequence of robot moves (R, L, U, D), determine whether the robot\nreturns to its starting position after completing all moves.\n\nReference: https://leetcode.com/problems/robot-return-to-origin/\n\nComplexity:\n    Time:  O(n) where n is the number of moves\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef judge_circle(moves: str) -> bool:\n    \"\"\"Determine whether a sequence of moves returns to the origin.\n\n    Args:\n        moves: A string of move characters ('U', 'D', 'L', 'R').\n\n    Returns:\n        True if the robot ends at the starting position, False otherwise.\n\n    Examples:\n        >>> judge_circle(\"UD\")\n        True\n    \"\"\"\n    move_counts = {\n        \"U\": 0,\n        \"D\": 0,\n        \"R\": 0,\n        \"L\": 0,\n    }\n    for char in moves:\n        move_counts[char] = move_counts[char] + 1\n    return move_counts[\"L\"] == move_counts[\"R\"] and move_counts[\"U\"] == move_counts[\"D\"]\n"
  },
  {
    "path": "algorithms/string/knuth_morris_pratt.py",
    "content": "\"\"\"\nKnuth-Morris-Pratt String Search\n\nGiven two sequences (text and pattern), return the list of start indexes in\ntext that match the pattern using the KMP algorithm.\n\nReference: https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm\n\nComplexity:\n    Time:  O(n + m) where n = len(text), m = len(pattern)\n    Space: O(m) for the prefix table\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Sequence\n\n\ndef knuth_morris_pratt(text: Sequence, pattern: Sequence) -> list[int]:\n    \"\"\"Find all occurrences of pattern in text using the KMP algorithm.\n\n    Args:\n        text: The sequence to search in.\n        pattern: The pattern sequence to search for.\n\n    Returns:\n        A list of starting indices where pattern occurs in text.\n\n    Examples:\n        >>> knuth_morris_pratt('hello there hero!', 'he')\n        [0, 7, 12]\n    \"\"\"\n    text_length = len(text)\n    pattern_length = len(pattern)\n    prefix_table = [0 for _ in range(pattern_length)]\n    match_length = 0\n\n    for index in range(1, pattern_length):\n        while match_length and pattern[index] != pattern[match_length]:\n            match_length = prefix_table[match_length - 1]\n        if pattern[index] == pattern[match_length]:\n            match_length += 1\n            prefix_table[index] = match_length\n\n    match_length = 0\n    matches: list[int] = []\n    for index in range(text_length):\n        while match_length and text[index] != pattern[match_length]:\n            match_length = prefix_table[match_length - 1]\n        if text[index] == pattern[match_length]:\n            match_length += 1\n            if match_length == pattern_length:\n                matches.append(index - pattern_length + 1)\n                match_length = prefix_table[match_length - 1]\n    return matches\n"
  },
  {
    "path": "algorithms/string/license_number.py",
    "content": "\"\"\"\nLicense Key Formatting\n\nGiven a license key string and a group size k, reformat the key so that each\ngroup contains exactly k characters, separated by dashes.\n\nReference: https://leetcode.com/problems/license-key-formatting/\n\nComplexity:\n    Time:  O(n) where n is the length of the key\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef license_number(key: str, group_size: int) -> str:\n    \"\"\"Reformat a license key string into groups of a given size.\n\n    Args:\n        key: The license key string with dashes.\n        group_size: The desired size of each group.\n\n    Returns:\n        The reformatted license key with groups separated by dashes.\n\n    Examples:\n        >>> license_number(\"a-bc-dfd-df\", 2)\n        'ab-cd-fd-df'\n    \"\"\"\n    result: list[str] = []\n    alphanumeric: list[str] = []\n    for char in key:\n        if char != \"-\":\n            alphanumeric.append(char)\n    for index, char in enumerate(reversed(alphanumeric)):\n        result.append(char)\n        if (index + 1) % group_size == 0 and index != len(alphanumeric) - 1:\n            result.append(\"-\")\n    return \"\".join(result[::-1])\n"
  },
  {
    "path": "algorithms/string/longest_common_prefix.py",
    "content": "\"\"\"\nLongest Common Prefix\n\nFind the longest common prefix string amongst an array of strings.\nThree approaches: horizontal scanning, vertical scanning, and divide and conquer.\n\nReference: https://leetcode.com/problems/longest-common-prefix/\n\nComplexity:\n    Time:  O(S) where S is the sum of all characters in all strings\n    Space: O(1) for iterative, O(m * log n) for divide and conquer\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _common_prefix(first: str, second: str) -> str:\n    \"\"\"Return the common prefix of two strings.\n\n    Args:\n        first: The first string.\n        second: The second string.\n\n    Returns:\n        The common prefix shared by both strings.\n    \"\"\"\n    if not first or not second:\n        return \"\"\n    index = 0\n    while first[index] == second[index]:\n        index = index + 1\n        if index >= len(first) or index >= len(second):\n            return first[0:index]\n    return first[0:index]\n\n\ndef longest_common_prefix_v1(strings: list[str]) -> str:\n    \"\"\"Find longest common prefix using horizontal scanning.\n\n    Args:\n        strings: A list of strings to compare.\n\n    Returns:\n        The longest common prefix, or empty string if none exists.\n\n    Examples:\n        >>> longest_common_prefix_v1([\"flower\", \"flow\", \"flight\"])\n        'fl'\n    \"\"\"\n    if not strings:\n        return \"\"\n    result = strings[0]\n    for index in range(len(strings)):\n        result = _common_prefix(result, strings[index])\n    return result\n\n\ndef longest_common_prefix_v2(strings: list[str]) -> str:\n    \"\"\"Find longest common prefix using vertical scanning.\n\n    Args:\n        strings: A list of strings to compare.\n\n    Returns:\n        The longest common prefix, or empty string if none exists.\n\n    Examples:\n        >>> longest_common_prefix_v2([\"flower\", \"flow\", \"flight\"])\n        'fl'\n    \"\"\"\n    if not strings:\n        return \"\"\n    for index in range(len(strings[0])):\n        for string in strings[1:]:\n            if index == len(string) or string[index] != strings[0][index]:\n                return strings[0][0:index]\n    return strings[0]\n\n\ndef longest_common_prefix_v3(strings: list[str]) -> str:\n    \"\"\"Find longest common prefix using divide and conquer.\n\n    Args:\n        strings: A list of strings to compare.\n\n    Returns:\n        The longest common prefix, or empty string if none exists.\n\n    Examples:\n        >>> longest_common_prefix_v3([\"flower\", \"flow\", \"flight\"])\n        'fl'\n    \"\"\"\n    if not strings:\n        return \"\"\n    return _longest_common_recursive(strings, 0, len(strings) - 1)\n\n\ndef _longest_common_recursive(strings: list[str], left: int, right: int) -> str:\n    \"\"\"Recursively find the longest common prefix using divide and conquer.\n\n    Args:\n        strings: The list of strings.\n        left: The left index of the current partition.\n        right: The right index of the current partition.\n\n    Returns:\n        The longest common prefix for the partition.\n    \"\"\"\n    if left == right:\n        return strings[left]\n    mid = (left + right) // 2\n    lcp_left = _longest_common_recursive(strings, left, mid)\n    lcp_right = _longest_common_recursive(strings, mid + 1, right)\n    return _common_prefix(lcp_left, lcp_right)\n"
  },
  {
    "path": "algorithms/string/longest_palindromic_substring.py",
    "content": "\"\"\"\nLongest Palindromic Substring\n\nGiven a string, find the longest palindromic substring using Manacher's\nalgorithm, which runs in linear time.\n\nReference: https://en.wikipedia.org/wiki/Longest_palindromic_substring\n\nComplexity:\n    Time:  O(n) using Manacher's algorithm\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef longest_palindrome(text: str) -> str:\n    \"\"\"Find the longest palindromic substring using Manacher's algorithm.\n\n    Args:\n        text: The input string to search.\n\n    Returns:\n        The longest palindromic substring.\n\n    Examples:\n        >>> longest_palindrome(\"cbbd\")\n        'bb'\n    \"\"\"\n    if len(text) < 2:\n        return text\n\n    expanded = \"#\" + \"#\".join(text) + \"#\"\n    palindrome_radii = [0] * len(expanded)\n    center, right_boundary = 0, 0\n    best_index, best_length = 0, 0\n\n    for index in range(len(expanded)):\n        if index < right_boundary and 2 * center - index < len(expanded):\n            palindrome_radii[index] = min(\n                right_boundary - index,\n                palindrome_radii[2 * center - index],\n            )\n        else:\n            palindrome_radii[index] = 1\n\n        while (\n            palindrome_radii[index] + index < len(expanded)\n            and index - palindrome_radii[index] >= 0\n            and expanded[index - palindrome_radii[index]]\n            == expanded[index + palindrome_radii[index]]\n        ):\n            palindrome_radii[index] += 1\n\n        if index + palindrome_radii[index] > right_boundary:\n            right_boundary = index + palindrome_radii[index]\n            center = index\n\n        if palindrome_radii[index] > best_length:\n            best_index = index\n            best_length = palindrome_radii[index]\n\n    substring = expanded[\n        best_index - palindrome_radii[best_index] + 1 : best_index\n        + palindrome_radii[best_index]\n    ]\n    return substring.replace(\"#\", \"\")\n"
  },
  {
    "path": "algorithms/string/make_sentence.py",
    "content": "\"\"\"\nMake Sentence\n\nFor a given string and dictionary, count how many sentences can be formed\nfrom the string such that all words are contained in the dictionary.\n\nReference: https://en.wikipedia.org/wiki/Word_break_problem\n\nComplexity:\n    Time:  O(2^n) worst case due to recursive exploration\n    Space: O(n) recursion depth\n\"\"\"\n\nfrom __future__ import annotations\n\ncount = 0\n\n\ndef make_sentence(text_piece: str, dictionaries: list[str]) -> bool:\n    \"\"\"Check if a string can be segmented into dictionary words and count ways.\n\n    Updates the global ``count`` variable with the number of valid segmentations.\n\n    Args:\n        text_piece: The string to segment.\n        dictionaries: A list of valid dictionary words.\n\n    Returns:\n        True if any segmentation is possible (always returns True).\n\n    Examples:\n        >>> make_sentence(\"applet\", [\"\", \"app\", \"let\", \"t\", \"apple\", \"applet\"])\n        True\n    \"\"\"\n    global count\n    if len(text_piece) == 0:\n        return True\n    for index in range(0, len(text_piece)):\n        prefix, suffix = text_piece[0:index], text_piece[index:]\n        if (prefix in dictionaries\n                and (suffix in dictionaries\n                     or make_sentence(suffix, dictionaries))):\n                count += 1\n    return True\n"
  },
  {
    "path": "algorithms/string/manacher.py",
    "content": "\"\"\"Manacher's algorithm — find the longest palindromic substring in O(n).\n\nManacher's algorithm uses the symmetry of palindromes to avoid redundant\ncomparisons, achieving linear time. It transforms the input to handle\nboth odd- and even-length palindromes uniformly.\n\nInspired by PR #931 (Simranstha045).\n\"\"\"\n\n\ndef manacher(s: str) -> str:\n    \"\"\"Return the longest palindromic substring of *s* in O(n) time.\"\"\"\n    # Transform \"abc\" -> \"^#a#b#c#$\" so every palindrome has odd length\n    t = \"^#\" + \"#\".join(s) + \"#$\"\n    n = len(t)\n    p = [0] * n  # p[i] = radius of palindrome centred at i\n    centre = right = 0\n    for i in range(1, n - 1):\n        mirror = 2 * centre - i\n        if i < right:\n            p[i] = min(right - i, p[mirror])\n        while t[i + p[i] + 1] == t[i - p[i] - 1]:\n            p[i] += 1\n        if i + p[i] > right:\n            centre, right = i, i + p[i]\n    max_len, centre_index = max((v, i) for i, v in enumerate(p))\n    start = (centre_index - max_len) // 2\n    return s[start : start + max_len]\n"
  },
  {
    "path": "algorithms/string/merge_string_checker.py",
    "content": "\"\"\"\nMerge String Checker\n\nDetermine if a given string can be formed by interleaving two other strings,\npreserving the character order from each part.\n\nReference: https://leetcode.com/problems/interleaving-string/\n\nComplexity:\n    Time:  O(2^n) worst case for recursive, similar for iterative\n    Space: O(n) for recursion depth / stack\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_merge_recursive(text: str, part1: str, part2: str) -> bool:\n    \"\"\"Check if text is an interleaving of part1 and part2 recursively.\n\n    Args:\n        text: The merged string to verify.\n        part1: The first source string.\n        part2: The second source string.\n\n    Returns:\n        True if text is a valid interleaving of part1 and part2.\n\n    Examples:\n        >>> is_merge_recursive(\"codewars\", \"cdw\", \"oears\")\n        True\n    \"\"\"\n    if not part1:\n        return text == part2\n    if not part2:\n        return text == part1\n    if not text:\n        return part1 + part2 == \"\"\n    if text[0] == part1[0] and is_merge_recursive(text[1:], part1[1:], part2):\n        return True\n    return (text[0] == part2[0]\n            and is_merge_recursive(text[1:], part1, part2[1:]))\n\n\ndef is_merge_iterative(text: str, part1: str, part2: str) -> bool:\n    \"\"\"Check if text is an interleaving of part1 and part2 iteratively.\n\n    Args:\n        text: The merged string to verify.\n        part1: The first source string.\n        part2: The second source string.\n\n    Returns:\n        True if text is a valid interleaving of part1 and part2.\n\n    Examples:\n        >>> is_merge_iterative(\"codewars\", \"cdw\", \"oears\")\n        True\n    \"\"\"\n    tuple_list = [(text, part1, part2)]\n    while tuple_list:\n        string, first_part, second_part = tuple_list.pop()\n        if string:\n            if first_part and string[0] == first_part[0]:\n                tuple_list.append((string[1:], first_part[1:], second_part))\n            if second_part and string[0] == second_part[0]:\n                tuple_list.append((string[1:], first_part, second_part[1:]))\n        else:\n            if not first_part and not second_part:\n                return True\n    return False\n"
  },
  {
    "path": "algorithms/string/min_distance.py",
    "content": "\"\"\"\nMinimum Edit Distance (Delete Operation)\n\nGiven two words, find the minimum number of steps required to make them the\nsame, where each step deletes one character from either string.\n\nReference: https://leetcode.com/problems/delete-operation-for-two-strings/\n\nComplexity:\n    Time:  O(2^n) for recursive LCS, O(m * n) for DP approach\n    Space: O(m * n) for DP table\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef min_distance(word1: str, word2: str) -> int:\n    \"\"\"Find minimum deletions to make two words equal via recursive LCS.\n\n    Args:\n        word1: The first word.\n        word2: The second word.\n\n    Returns:\n        The minimum number of deletion steps.\n\n    Examples:\n        >>> min_distance(\"sea\", \"eat\")\n        2\n    \"\"\"\n    return len(word1) + len(word2) - 2 * _lcs(word1, word2, len(word1), len(word2))\n\n\ndef _lcs(word1: str, word2: str, length1: int, length2: int) -> int:\n    \"\"\"Compute the length of the longest common subsequence recursively.\n\n    Args:\n        word1: The first word.\n        word2: The second word.\n        length1: Current length to consider in word1.\n        length2: Current length to consider in word2.\n\n    Returns:\n        The length of the longest common subsequence.\n    \"\"\"\n    if length1 == 0 or length2 == 0:\n        return 0\n    if word1[length1 - 1] == word2[length2 - 1]:\n        return 1 + _lcs(word1, word2, length1 - 1, length2 - 1)\n    return max(\n        _lcs(word1, word2, length1 - 1, length2),\n        _lcs(word1, word2, length1, length2 - 1),\n    )\n\n\ndef min_distance_dp(word1: str, word2: str) -> int:\n    \"\"\"Find minimum deletions to make two words equal using dynamic programming.\n\n    Args:\n        word1: The first word.\n        word2: The second word.\n\n    Returns:\n        The minimum number of deletion steps.\n\n    Examples:\n        >>> min_distance_dp(\"sea\", \"eat\")\n        2\n    \"\"\"\n    rows, cols = len(word1) + 1, len(word2) + 1\n    table = [[0 for _ in range(cols)] for _ in range(rows)]\n\n    if rows == cols:\n        for index in range(1, rows):\n            table[index][0], table[0][index] = index, index\n    else:\n        for index in range(rows):\n            table[index][0] = index\n        for index in range(cols):\n            table[0][index] = index\n\n    for row in range(1, rows):\n        for col in range(1, cols):\n            if word1[row - 1] == word2[col - 1]:\n                table[row][col] = table[row - 1][col - 1]\n            else:\n                table[row][col] = min(table[row - 1][col], table[row][col - 1]) + 1\n\n    return table[len(word1)][len(word2)]\n"
  },
  {
    "path": "algorithms/string/multiply_strings.py",
    "content": "\"\"\"\nMultiply Strings\n\nGiven two non-negative integers represented as strings, return their product\nas a string without using built-in BigInteger or direct integer conversion.\n\nReference: https://leetcode.com/problems/multiply-strings/\n\nComplexity:\n    Time:  O(m * n) where m, n are the lengths of the two numbers\n    Space: O(m + n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef multiply(num1: str, num2: str) -> str:\n    \"\"\"Multiply two numbers represented as strings.\n\n    Args:\n        num1: The first number as a string.\n        num2: The second number as a string.\n\n    Returns:\n        The product of the two numbers as a string.\n\n    Examples:\n        >>> multiply(\"23\", \"23\")\n        '529'\n    \"\"\"\n    intermediate: list[int] = []\n    zero = ord(\"0\")\n    position_i = 1\n    for digit_i in reversed(num1):\n        position_j = 1\n        accumulator = 0\n        for digit_j in reversed(num2):\n            product = (\n                (ord(digit_i) - zero) * (ord(digit_j) - zero) * position_j * position_i\n            )\n            position_j *= 10\n            accumulator += product\n        position_i *= 10\n        intermediate.append(accumulator)\n    return str(sum(intermediate))\n"
  },
  {
    "path": "algorithms/string/one_edit_distance.py",
    "content": "\"\"\"\nOne Edit Distance\n\nGiven two strings, determine if they are exactly one edit distance apart.\nAn edit is an insertion, deletion, or replacement of a single character.\n\nReference: https://leetcode.com/problems/one-edit-distance/\n\nComplexity:\n    Time:  O(n) where n is the length of the shorter string\n    Space: O(n) for string slicing\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef is_one_edit(source: str, target: str) -> bool:\n    \"\"\"Check if two strings are exactly one edit apart using slicing.\n\n    Args:\n        source: The first string.\n        target: The second string.\n\n    Returns:\n        True if the strings are exactly one edit apart, False otherwise.\n\n    Examples:\n        >>> is_one_edit(\"abc\", \"abd\")\n        True\n    \"\"\"\n    if len(source) > len(target):\n        return is_one_edit(target, source)\n    if len(target) - len(source) > 1 or target == source:\n        return False\n    for index in range(len(source)):\n        if source[index] != target[index]:\n            return (\n                source[index + 1 :] == target[index + 1 :]\n                or source[index:] == target[index + 1 :]\n            )\n    return True\n\n\ndef is_one_edit2(source: str, target: str) -> bool:\n    \"\"\"Check if two strings are exactly one edit apart using modification.\n\n    Args:\n        source: The first string.\n        target: The second string.\n\n    Returns:\n        True if the strings are exactly one edit apart, False otherwise.\n\n    Examples:\n        >>> is_one_edit2(\"abc\", \"abd\")\n        True\n    \"\"\"\n    source_length, target_length = len(source), len(target)\n    if source_length > target_length:\n        return is_one_edit2(target, source)\n    if len(target) - len(source) > 1 or target == source:\n        return False\n    for index in range(len(source)):\n        if source[index] != target[index]:\n            if source_length == target_length:\n                source = source[:index] + target[index] + source[index + 1 :]\n            else:\n                source = source[:index] + target[index] + source[index:]\n            break\n    return source == target or source == target[:-1]\n"
  },
  {
    "path": "algorithms/string/panagram.py",
    "content": "\"\"\"\nPanagram Checker\n\nCheck whether a given string is a panagram (a sentence using every letter\nof the English alphabet at least once).\n\nReference: https://en.wikipedia.org/wiki/Pangram\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(1) since the letter set is bounded at 26\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom string import ascii_lowercase\n\n\ndef panagram(string: str) -> bool:\n    \"\"\"Check if the input string is an English panagram.\n\n    Args:\n        string: A sentence to check.\n\n    Returns:\n        True if the string contains every English letter, False otherwise.\n\n    Examples:\n        >>> panagram(\"the quick brown fox jumps over the lazy dog\")\n        True\n    \"\"\"\n    letters = set(ascii_lowercase)\n    for char in string:\n        letters.discard(char.lower())\n    return len(letters) == 0\n"
  },
  {
    "path": "algorithms/string/rabin_karp.py",
    "content": "\"\"\"\nRabin-Karp String Search\n\nA string searching algorithm that uses hashing to find a pattern in text.\nUses a rolling hash to efficiently compare the pattern hash with substrings.\n\nReference: https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm\n\nComplexity:\n    Time:  O(n + m) average, O(n * m) worst case\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\nclass RollingHash:\n    \"\"\"A rolling hash implementation for the Rabin-Karp algorithm.\n\n    Args:\n        text: The text to compute the hash over.\n        window_size: The size of the hash window.\n    \"\"\"\n\n    def __init__(self, text: str, window_size: int) -> None:\n        self.text = text\n        self.hash = 0\n        self.window_size = window_size\n\n        for index in range(0, window_size):\n            self.hash += (ord(self.text[index]) - ord(\"a\") + 1) * (\n                26 ** (window_size - index - 1)\n            )\n\n        self.window_start = 0\n        self.window_end = window_size\n\n    def move_window(self) -> None:\n        \"\"\"Slide the hash window one position to the right.\"\"\"\n        if self.window_end <= len(self.text) - 1:\n            self.hash -= (ord(self.text[self.window_start]) - ord(\"a\") + 1) * 26 ** (\n                self.window_size - 1\n            )\n            self.hash *= 26\n            self.hash += ord(self.text[self.window_end]) - ord(\"a\") + 1\n            self.window_start += 1\n            self.window_end += 1\n\n    def window_text(self) -> str:\n        \"\"\"Return the current text within the hash window.\n\n        Returns:\n            The substring currently covered by the rolling hash window.\n        \"\"\"\n        return self.text[self.window_start : self.window_end]\n\n\ndef rabin_karp(word: str, text: str) -> int | None:\n    \"\"\"Find the first occurrence of word in text using the Rabin-Karp algorithm.\n\n    Args:\n        word: The pattern to search for.\n        text: The text to search in.\n\n    Returns:\n        The index of the first occurrence, or None if not found.\n\n    Examples:\n        >>> rabin_karp(\"abc\", \"zsnabckfkd\")\n        3\n    \"\"\"\n    if word == \"\" or text == \"\":\n        return None\n    if len(word) > len(text):\n        return None\n\n    rolling_hash = RollingHash(text, len(word))\n    word_hash = RollingHash(word, len(word))\n\n    for _ in range(len(text) - len(word) + 1):\n        if (rolling_hash.hash == word_hash.hash\n                and rolling_hash.window_text() == word):\n                return rolling_hash.window_start\n        rolling_hash.move_window()\n    return None\n"
  },
  {
    "path": "algorithms/string/repeat_string.py",
    "content": "\"\"\"\nRepeated String Match\n\nGiven two strings A and B, find the minimum number of times A has to be\nrepeated such that B is a substring of the result. Return -1 if impossible.\n\nReference: https://leetcode.com/problems/repeated-string-match/\n\nComplexity:\n    Time:  O(n * m) where n = len(A), m = len(B)\n    Space: O(n * (m/n + 2))\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef repeat_string(base: str, target: str) -> int:\n    \"\"\"Find minimum repetitions of base needed to contain target as a substring.\n\n    Args:\n        base: The string to repeat.\n        target: The string that should appear as a substring.\n\n    Returns:\n        The minimum number of repetitions, or -1 if not possible.\n\n    Examples:\n        >>> repeat_string(\"abcd\", \"cdabcdab\")\n        3\n    \"\"\"\n    repetition_count = 1\n    repeated = base\n    max_count = (len(target) / len(base)) + 1\n    while target not in repeated:\n        repeated = repeated + base\n        if repetition_count > max_count:\n            repetition_count = -1\n            break\n        repetition_count = repetition_count + 1\n\n    return repetition_count\n"
  },
  {
    "path": "algorithms/string/repeat_substring.py",
    "content": "\"\"\"\nRepeated Substring Pattern\n\nGiven a non-empty string, check if it can be constructed by taking a\nsubstring of it and appending multiple copies of the substring together.\n\nReference: https://leetcode.com/problems/repeated-substring-pattern/\n\nComplexity:\n    Time:  O(n) for the string containment check\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef repeat_substring(text: str) -> bool:\n    \"\"\"Check if a string is composed of a repeated substring pattern.\n\n    Args:\n        text: The input string to check.\n\n    Returns:\n        True if the string is a repeated pattern, False otherwise.\n\n    Examples:\n        >>> repeat_substring(\"abab\")\n        True\n    \"\"\"\n    doubled = (text + text)[1:-1]\n    return text in doubled\n"
  },
  {
    "path": "algorithms/string/reverse_string.py",
    "content": "\"\"\"\nReverse String\n\nReverse a string using four different approaches: recursive, iterative,\npythonic (using reversed), and ultra-pythonic (using slicing).\n\nReference: https://en.wikipedia.org/wiki/String_(computer_science)#Reversal\n\nComplexity:\n    Time:  O(n) for all approaches\n    Space: O(n) for all approaches, O(log n) stack for recursive\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef recursive(text: str) -> str:\n    \"\"\"Reverse a string using a recursive divide-and-conquer approach.\n\n    Args:\n        text: The string to reverse.\n\n    Returns:\n        The reversed string.\n\n    Examples:\n        >>> recursive(\"hello there\")\n        'ereht olleh'\n    \"\"\"\n    length = len(text)\n    if length < 2:\n        return text\n    return recursive(text[length // 2 :]) + recursive(text[: length // 2])\n\n\ndef iterative(text: str) -> str:\n    \"\"\"Reverse a string using iterative character swapping.\n\n    Args:\n        text: The string to reverse.\n\n    Returns:\n        The reversed string.\n\n    Examples:\n        >>> iterative(\"hello there\")\n        'ereht olleh'\n    \"\"\"\n    characters = list(text)\n    left, right = 0, len(text) - 1\n    while left < right:\n        characters[left], characters[right] = (\n            characters[right],\n            characters[left],\n        )\n        left += 1\n        right -= 1\n    return \"\".join(characters)\n\n\ndef pythonic(text: str) -> str:\n    \"\"\"Reverse a string using the built-in reversed function.\n\n    Args:\n        text: The string to reverse.\n\n    Returns:\n        The reversed string.\n\n    Examples:\n        >>> pythonic(\"hello there\")\n        'ereht olleh'\n    \"\"\"\n    characters = list(reversed(text))\n    return \"\".join(characters)\n\n\ndef ultra_pythonic(text: str) -> str:\n    \"\"\"Reverse a string using slice notation.\n\n    Args:\n        text: The string to reverse.\n\n    Returns:\n        The reversed string.\n\n    Examples:\n        >>> ultra_pythonic(\"hello there\")\n        'ereht olleh'\n    \"\"\"\n    return text[::-1]\n"
  },
  {
    "path": "algorithms/string/reverse_vowel.py",
    "content": "\"\"\"\nReverse Vowels of a String\n\nGiven a string, reverse only the vowels while keeping all other characters\nin their original positions.\n\nReference: https://leetcode.com/problems/reverse-vowels-of-a-string/\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(n) for the character list\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef reverse_vowel(text: str) -> str:\n    \"\"\"Reverse only the vowels in a string.\n\n    Args:\n        text: The input string.\n\n    Returns:\n        A new string with vowels reversed.\n\n    Examples:\n        >>> reverse_vowel(\"hello\")\n        'holle'\n    \"\"\"\n    vowels = \"AEIOUaeiou\"\n    left, right = 0, len(text) - 1\n    characters = list(text)\n    while left < right:\n        while left < right and characters[left] not in vowels:\n            left += 1\n        while left < right and characters[right] not in vowels:\n            right -= 1\n        characters[left], characters[right] = (\n            characters[right],\n            characters[left],\n        )\n        left, right = left + 1, right - 1\n    return \"\".join(characters)\n"
  },
  {
    "path": "algorithms/string/reverse_words.py",
    "content": "\"\"\"\nReverse Words in a String\n\nGiven a string, reverse the order of words. Leading and trailing spaces\nare trimmed and words are separated by single spaces.\n\nReference: https://leetcode.com/problems/reverse-words-in-a-string/\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef _reverse_list(array: list[str], left: int, right: int) -> None:\n    \"\"\"Reverse a portion of a list in place.\n\n    Args:\n        array: The list to modify.\n        left: The starting index.\n        right: The ending index.\n    \"\"\"\n    while left < right:\n        array[left], array[right] = array[right], array[left]\n        left += 1\n        right -= 1\n\n\ndef reverse_words(string: str) -> str:\n    \"\"\"Reverse the order of words in a string.\n\n    Args:\n        string: The input string of words.\n\n    Returns:\n        A string with the word order reversed.\n\n    Examples:\n        >>> reverse_words(\"I am keon kim and I like pizza\")\n        'pizza like I and kim keon am I'\n    \"\"\"\n    words = string.strip().split()\n    word_count = len(words)\n    _reverse_list(words, 0, word_count - 1)\n    return \" \".join(words)\n"
  },
  {
    "path": "algorithms/string/roman_to_int.py",
    "content": "\"\"\"\nRoman Numeral to Integer\n\nGiven a Roman numeral string, convert it to an integer. Input is guaranteed\nto be within the range from 1 to 3999.\n\nReference: https://en.wikipedia.org/wiki/Roman_numerals\n\nComplexity:\n    Time:  O(n) where n is the length of the Roman numeral string\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef roman_to_int(text: str) -> int:\n    \"\"\"Convert a Roman numeral string to an integer.\n\n    Args:\n        text: A valid Roman numeral string.\n\n    Returns:\n        The integer value of the Roman numeral.\n\n    Examples:\n        >>> roman_to_int(\"DCXXI\")\n        621\n    \"\"\"\n    number = 0\n    roman_values = {\n        \"M\": 1000,\n        \"D\": 500,\n        \"C\": 100,\n        \"L\": 50,\n        \"X\": 10,\n        \"V\": 5,\n        \"I\": 1,\n    }\n    for index in range(len(text) - 1):\n        if roman_values[text[index]] < roman_values[text[index + 1]]:\n            number -= roman_values[text[index]]\n        else:\n            number += roman_values[text[index]]\n    return number + roman_values[text[-1]]\n"
  },
  {
    "path": "algorithms/string/rotate.py",
    "content": "\"\"\"\nRotate String\n\nGiven a string and an integer k, return the string rotated by k positions\nto the left. Two approaches are provided.\n\nReference: https://en.wikipedia.org/wiki/Circular_shift\n\nComplexity:\n    Time:  O(n) where n is the length of the string\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef rotate(text: str, positions: int) -> str:\n    \"\"\"Rotate a string left by k positions using repeated string approach.\n\n    Args:\n        text: The string to rotate.\n        positions: The number of positions to rotate left.\n\n    Returns:\n        The rotated string.\n\n    Examples:\n        >>> rotate(\"hello\", 2)\n        'llohe'\n    \"\"\"\n    long_string = text * (positions // len(text) + 2)\n    if positions <= len(text):\n        return long_string[positions : positions + len(text)]\n    else:\n        return long_string[positions - len(text) : positions]\n\n\ndef rotate_alt(string: str, positions: int) -> str:\n    \"\"\"Rotate a string left by k positions using modular arithmetic.\n\n    Args:\n        string: The string to rotate.\n        positions: The number of positions to rotate left.\n\n    Returns:\n        The rotated string.\n\n    Examples:\n        >>> rotate_alt(\"hello\", 2)\n        'llohe'\n    \"\"\"\n    positions = positions % len(string)\n    return string[positions:] + string[:positions]\n"
  },
  {
    "path": "algorithms/string/strip_url_params.py",
    "content": "\"\"\"\nStrip URL Parameters\n\nRemove duplicate query string parameters from a URL and optionally remove\nspecified parameters. Three approaches of increasing Pythonic style.\n\nReference: https://en.wikipedia.org/wiki/Query_string\n\nComplexity:\n    Time:  O(n) where n is the length of the URL\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport urllib\nimport urllib.parse\nfrom collections import defaultdict\n\n\ndef strip_url_params1(url: str, params_to_strip: list[str] | None = None) -> str:\n    \"\"\"Remove duplicate and specified URL parameters using manual parsing.\n\n    Args:\n        url: The URL string to process.\n        params_to_strip: Optional list of parameter names to remove.\n\n    Returns:\n        The URL with duplicate and specified parameters removed.\n\n    Examples:\n        >>> strip_url_params1(\"www.saadbenn.com?a=1&b=2&a=2\")\n        'www.saadbenn.com?a=1&b=2'\n    \"\"\"\n    if not params_to_strip:\n        params_to_strip = []\n    if url:\n        result = \"\"\n        tokens = url.split(\"?\")\n        domain = tokens[0]\n        query_string = tokens[-1]\n        result += domain\n        if len(tokens) > 1:\n            result += \"?\"\n        if not query_string:\n            return url\n        else:\n            key_value_pairs: list[str] = []\n            fragment = \"\"\n            for char in query_string:\n                if char.isdigit():\n                    key_value_pairs.append(fragment + char)\n                    fragment = \"\"\n                else:\n                    fragment += char\n            seen: dict[str, int] = defaultdict(int)\n            for pair in key_value_pairs:\n                token_parts = pair.split(\"=\")\n                if token_parts[0]:\n                    length = len(token_parts[0])\n                    if length == 1:\n                        if token_parts and (token_parts[0] not in seen):\n                            if params_to_strip:\n                                if token_parts[0] != params_to_strip[0]:\n                                    seen[token_parts[0]] = token_parts[1]\n                                    result = (\n                                        result + token_parts[0] + \"=\" + token_parts[1]\n                                    )\n                            else:\n                                if token_parts[0] not in seen:\n                                    seen[token_parts[0]] = token_parts[1]\n                                    result = (\n                                        result + token_parts[0] + \"=\" + token_parts[1]\n                                    )\n                    else:\n                        check = token_parts[0]\n                        letter = check[1]\n                        if token_parts and (letter not in seen):\n                            if params_to_strip:\n                                if letter != params_to_strip[0]:\n                                    seen[letter] = token_parts[1]\n                                    result = (\n                                        result + token_parts[0] + \"=\" + token_parts[1]\n                                    )\n                            else:\n                                if letter not in seen:\n                                    seen[letter] = token_parts[1]\n                                    result = (\n                                        result + token_parts[0] + \"=\" + token_parts[1]\n                                    )\n    return result\n\n\ndef strip_url_params2(url: str, param_to_strip: list[str] | None = None) -> str:\n    \"\"\"Remove duplicate and specified URL parameters using list operations.\n\n    Args:\n        url: The URL string to process.\n        param_to_strip: Optional list of parameter names to remove.\n\n    Returns:\n        The URL with duplicate and specified parameters removed.\n\n    Examples:\n        >>> strip_url_params2(\"www.saadbenn.com?a=1&b=2&a=2\")\n        'www.saadbenn.com?a=1&b=2'\n    \"\"\"\n    if param_to_strip is None:\n        param_to_strip = []\n    if \"?\" not in url:\n        return url\n\n    queries = (url.split(\"?\")[1]).split(\"&\")\n    query_keys = [query[0] for query in queries]\n    for index in range(len(query_keys) - 1, 0, -1):\n        if (\n            query_keys[index] in param_to_strip\n            or query_keys[index] in query_keys[0:index]\n        ):\n            queries.pop(index)\n\n    return url.split(\"?\")[0] + \"?\" + \"&\".join(queries)\n\n\ndef strip_url_params3(url: str, strip: list[str] | None = None) -> str:\n    \"\"\"Remove duplicate and specified URL parameters using urllib.\n\n    Args:\n        url: The URL string to process.\n        strip: Optional list of parameter names to remove.\n\n    Returns:\n        The URL with duplicate and specified parameters removed.\n\n    Examples:\n        >>> strip_url_params3(\"www.saadbenn.com?a=1&b=2&a=2\")\n        'www.saadbenn.com?a=1&b=2'\n    \"\"\"\n    if not strip:\n        strip = []\n\n    parsed = urllib.parse.urlparse(url)\n    query = urllib.parse.parse_qs(parsed.query)\n\n    query = {key: values[0] for key, values in query.items() if key not in strip}\n    query_string = urllib.parse.urlencode(query)\n    new_parsed = parsed._replace(query=query_string)\n\n    return new_parsed.geturl()\n"
  },
  {
    "path": "algorithms/string/strong_password.py",
    "content": "\"\"\"\nStrong Password Checker\n\nGiven a password string, determine the minimum number of characters that\nmust be added to make it strong. A strong password has at least 6 characters,\na digit, a lowercase letter, an uppercase letter, and a special character.\n\nReference: https://www.hackerrank.com/challenges/strong-password/problem\n\nComplexity:\n    Time:  O(n) where n is the length of the password\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef strong_password(length: int, password: str) -> int:\n    \"\"\"Calculate minimum characters to add for a strong password.\n\n    Args:\n        length: The current length of the password.\n        password: The password string to evaluate.\n\n    Returns:\n        The minimum number of characters to add.\n\n    Examples:\n        >>> strong_password(3, \"Ab1\")\n        3\n    \"\"\"\n    missing_types = 0\n    if not any(char.isdigit() for char in password):\n        missing_types += 1\n    if not any(char.islower() for char in password):\n        missing_types += 1\n    if not any(char.isupper() for char in password):\n        missing_types += 1\n    if not any(char in \"!@#$%^&*()-+\" for char in password):\n        missing_types += 1\n    return max(missing_types, 6 - length)\n"
  },
  {
    "path": "algorithms/string/swap_characters.py",
    "content": "\"\"\"Swap characters — check if two strings can be made equal by one swap.\n\nGiven two strings of equal length, determine whether exactly one swap\nof two characters in the first string can make it equal to the second.\n\nInspired by PR #890 (Thejas-1).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef can_swap_to_equal(s: str, t: str) -> bool:\n    \"\"\"Return True if swapping exactly one pair in *s* yields *t*.\"\"\"\n    if len(s) != len(t):\n        return False\n    diffs = [(a, b) for a, b in zip(s, t, strict=False) if a != b]\n    return len(diffs) == 2 and diffs[0] == diffs[1][::-1]\n"
  },
  {
    "path": "algorithms/string/text_justification.py",
    "content": "\"\"\"\nText Justification\n\nGiven an array of words and a max width, format the text such that each line\nhas exactly max_width characters and is fully justified. Extra spaces are\ndistributed as evenly as possible with left slots getting more.\n\nReference: https://leetcode.com/problems/text-justification/\n\nComplexity:\n    Time:  O(n) where n is the total number of characters\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef text_justification(words: list[str], max_width: int) -> list[str]:\n    \"\"\"Justify text to a fixed width with evenly distributed spaces.\n\n    Args:\n        words: A list of words to justify.\n        max_width: The maximum width of each line.\n\n    Returns:\n        A list of fully justified strings.\n\n    Raises:\n        ValueError: If any word is longer than max_width.\n\n    Examples:\n        >>> text_justification([\"What\", \"must\", \"be\"], 16)\n        ['What   must   be']\n    \"\"\"\n    result: list[str] = []\n    row_length = 0\n    row_words: list[str] = []\n    index = 0\n    is_first_word = True\n\n    while index < len(words):\n        while row_length <= max_width and index < len(words):\n            if len(words[index]) > max_width:\n                raise ValueError(\n                    \"there exists word whose length is larger than max_width\"\n                )\n            tentative_length = row_length\n            row_words.append(words[index])\n            tentative_length += len(words[index])\n            if not is_first_word:\n                tentative_length += 1\n            if tentative_length > max_width:\n                row_words.pop()\n                break\n            row_length = tentative_length\n            index += 1\n            is_first_word = False\n\n        row = \"\"\n        if index == len(words):\n            for word in row_words:\n                row += word + \" \"\n            row = row[:-1]\n            row += \" \" * (max_width - len(row))\n        elif len(row_words) != 1:\n            extra_spaces = max_width - row_length\n            spaces_per_gap = extra_spaces // (len(row_words) - 1)\n            remaining_spaces = extra_spaces - spaces_per_gap * (len(row_words) - 1)\n            for word_index in range(len(row_words)):\n                row += row_words[word_index]\n                if word_index != len(row_words) - 1:\n                    row += \" \" * (1 + spaces_per_gap)\n                if remaining_spaces > 0:\n                    row += \" \"\n                    remaining_spaces -= 1\n        else:\n            row += row_words[0]\n            row += \" \" * (max_width - len(row))\n\n        result.append(row)\n        row_length = 0\n        row_words = []\n        is_first_word = True\n\n    return result\n"
  },
  {
    "path": "algorithms/string/unique_morse.py",
    "content": "\"\"\"\nUnique Morse Code Representations\n\nGiven a list of words, determine the number of unique Morse code\ntransformations among all the words.\n\nReference: https://leetcode.com/problems/unique-morse-code-words/\n\nComplexity:\n    Time:  O(n * k) where n is the number of words, k is average word length\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\n_MORSE_CODE = {\n    \"a\": \".-\",\n    \"b\": \"-...\",\n    \"c\": \"-.-.\",\n    \"d\": \"-..\",\n    \"e\": \".\",\n    \"f\": \"..-.\",\n    \"g\": \"--.\",\n    \"h\": \"....\",\n    \"i\": \"..\",\n    \"j\": \".---\",\n    \"k\": \"-.-\",\n    \"l\": \".-..\",\n    \"m\": \"--\",\n    \"n\": \"-.\",\n    \"o\": \"---\",\n    \"p\": \".--.\",\n    \"q\": \"--.-\",\n    \"r\": \".-.\",\n    \"s\": \"...\",\n    \"t\": \"-\",\n    \"u\": \"..-\",\n    \"v\": \"...-\",\n    \"w\": \".--\",\n    \"x\": \"-..-\",\n    \"y\": \"-.--\",\n    \"z\": \"--..\",\n}\n\n\ndef convert_morse_word(word: str) -> str:\n    \"\"\"Convert a word to its Morse code representation.\n\n    Args:\n        word: The word to convert (case-insensitive).\n\n    Returns:\n        The Morse code string for the word.\n\n    Examples:\n        >>> convert_morse_word(\"gin\")\n        '--...-.'\n    \"\"\"\n    morse_word = \"\"\n    word = word.lower()\n    for char in word:\n        morse_word = morse_word + _MORSE_CODE[char]\n    return morse_word\n\n\ndef unique_morse(words: list[str]) -> int:\n    \"\"\"Count the number of unique Morse code transformations.\n\n    Args:\n        words: A list of words to transform and count.\n\n    Returns:\n        The number of distinct Morse code representations.\n\n    Examples:\n        >>> unique_morse([\"gin\", \"zen\", \"gig\", \"msg\"])\n        2\n    \"\"\"\n    unique_transformations: list[str] = []\n    for word in words:\n        morse_word = convert_morse_word(word)\n        if morse_word not in unique_transformations:\n            unique_transformations.append(morse_word)\n    return len(unique_transformations)\n"
  },
  {
    "path": "algorithms/string/validate_coordinates.py",
    "content": "\"\"\"\nValidate Coordinates\n\nValidate if given parameters are valid geographical coordinates.\nLatitude must be between -90 and 90, longitude between -180 and 180.\n\nReference: https://en.wikipedia.org/wiki/Geographic_coordinate_system\n\nComplexity:\n    Time:  O(n) where n is the length of the coordinate string\n    Space: O(1)\n\"\"\"\n\nfrom __future__ import annotations\n\nimport re\n\n\ndef is_valid_coordinates_0(coordinates: str) -> bool:\n    \"\"\"Validate coordinates by character checking and parsing.\n\n    Args:\n        coordinates: A string of the form \"lat, lng\".\n\n    Returns:\n        True if the coordinates are valid, False otherwise.\n\n    Examples:\n        >>> is_valid_coordinates_0(\"-23, 25\")\n        True\n    \"\"\"\n    for char in coordinates:\n        if not (char.isdigit() or char in [\"-\", \".\", \",\", \" \"]):\n            return False\n    parts = coordinates.split(\", \")\n    if len(parts) != 2:\n        return False\n    try:\n        latitude = float(parts[0])\n        longitude = float(parts[1])\n    except ValueError:\n        return False\n    return -90 <= latitude <= 90 and -180 <= longitude <= 180\n\n\ndef is_valid_coordinates_1(coordinates: str) -> bool:\n    \"\"\"Validate coordinates by splitting and converting to floats.\n\n    Args:\n        coordinates: A string of the form \"lat, lng\".\n\n    Returns:\n        True if the coordinates are valid, False otherwise.\n\n    Examples:\n        >>> is_valid_coordinates_1(\"43.91343345, 143\")\n        True\n    \"\"\"\n    try:\n        latitude, longitude = [\n            abs(float(part)) for part in coordinates.split(\",\") if \"e\" not in part\n        ]\n    except ValueError:\n        return False\n    return latitude <= 90 and longitude <= 180\n\n\ndef is_valid_coordinates_regular_expression(coordinates: str) -> bool:\n    \"\"\"Validate coordinates using a regular expression.\n\n    Args:\n        coordinates: A string of the form \"lat, lng\".\n\n    Returns:\n        True if the coordinates are valid, False otherwise.\n\n    Examples:\n        >>> is_valid_coordinates_regular_expression(\"4, -3\")\n        True\n    \"\"\"\n    return bool(\n        re.match(\n            r\"-?(\\d|[1-8]\\d|90)\\.?\\d*, -?(\\d|[1-9]\\d|1[0-7]\\d|180)\\.?\\d*$\",\n            coordinates,\n        )\n    )\n"
  },
  {
    "path": "algorithms/string/word_squares.py",
    "content": "\"\"\"\nWord Squares\n\nGiven a set of words (without duplicates), find all word squares that can be\nbuilt from them. A word square reads the same horizontally and vertically.\n\nReference: https://leetcode.com/problems/word-squares/\n\nComplexity:\n    Time:  O(n * 26^L) where n is the number of words, L is word length\n    Space: O(n * L) for the prefix map\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\n\ndef word_squares(words: list[str]) -> list[list[str]]:\n    \"\"\"Find all valid word squares from a list of same-length words.\n\n    Args:\n        words: A list of words, all having the same length.\n\n    Returns:\n        A list of word squares, where each square is a list of words.\n\n    Examples:\n        >>> word_squares([\"area\", \"lead\", \"wall\", \"lady\", \"ball\"])\n        [['wall', 'area', 'lead', 'lady'], ['ball', 'area', 'lead', 'lady']]\n    \"\"\"\n    word_length = len(words[0])\n    prefix_map: dict[str, list[str]] = collections.defaultdict(list)\n    for word in words:\n        for index in range(word_length):\n            prefix_map[word[:index]].append(word)\n\n    def _build(square: list[str]) -> None:\n        if len(square) == word_length:\n            squares.append(square)\n            return\n        prefix = \"\"\n        for row in range(len(square)):\n            prefix += square[row][len(square)]\n        for word in prefix_map[prefix]:\n            _build(square + [word])\n\n    squares: list[list[str]] = []\n    for word in words:\n        _build([word])\n    return squares\n"
  },
  {
    "path": "algorithms/string/z_algorithm.py",
    "content": "\"\"\"Z-algorithm — linear-time pattern matching via the Z-array.\n\nThe Z-array for a string S stores at Z[i] the length of the longest\nsubstring starting at S[i] that is also a prefix of S. By concatenating\npattern + '$' + text, occurrences of the pattern correspond to positions\nwhere Z[i] == len(pattern).\n\nInspired by PR #930 (Simranstha045).\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef compute_z_array(s: str) -> list[int]:\n    \"\"\"Compute the Z-array for string *s* in O(n) time.\"\"\"\n    n = len(s)\n    if n == 0:\n        return []\n    z = [0] * n\n    z[0] = n\n    left = right = 0\n    for i in range(1, n):\n        if i < right:\n            z[i] = min(right - i, z[i - left])\n        while i + z[i] < n and s[z[i]] == s[i + z[i]]:\n            z[i] += 1\n        if i + z[i] > right:\n            left, right = i, i + z[i]\n    return z\n\n\ndef z_search(text: str, pattern: str) -> list[int]:\n    \"\"\"Return all starting indices where *pattern* occurs in *text*.\"\"\"\n    if not pattern or not text:\n        return []\n    concat = pattern + \"$\" + text\n    z = compute_z_array(concat)\n    m = len(pattern)\n    return [i - m - 1 for i in range(m + 1, len(concat)) if z[i] == m]\n"
  },
  {
    "path": "algorithms/tree/__init__.py",
    "content": "\"\"\"\nTree Algorithms\n\nA collection of binary tree and general tree algorithms including traversal,\nsearch, construction, and property-checking operations.\n\"\"\"\n\nfrom algorithms.data_structures.b_tree import BTree\nfrom algorithms.data_structures.b_tree import Node as BTreeNode\nfrom algorithms.tree.bin_tree_to_list import bin_tree_to_list\nfrom algorithms.tree.binary_tree_paths import binary_tree_paths\nfrom algorithms.tree.binary_tree_views import (\n    bottom_view,\n    left_view,\n    right_view,\n    top_view,\n)\nfrom algorithms.tree.construct_tree_postorder_preorder import (\n    construct_tree,\n    construct_tree_util,\n)\nfrom algorithms.tree.deepest_left import DeepestLeft, find_deepest_left\nfrom algorithms.tree.invert_tree import reverse\nfrom algorithms.tree.is_balanced import is_balanced\nfrom algorithms.tree.is_subtree import is_subtree\nfrom algorithms.tree.is_symmetric import is_symmetric, is_symmetric_iterative\nfrom algorithms.tree.longest_consecutive import longest_consecutive\nfrom algorithms.tree.lowest_common_ancestor import lca\nfrom algorithms.tree.max_height import max_height\nfrom algorithms.tree.max_path_sum import max_path_sum\nfrom algorithms.tree.min_height import min_depth, min_height\nfrom algorithms.tree.path_sum import has_path_sum, has_path_sum2, has_path_sum3\nfrom algorithms.tree.path_sum2 import path_sum, path_sum2, path_sum3\nfrom algorithms.tree.pretty_print import tree_print\nfrom algorithms.tree.same_tree import is_same_tree\nfrom algorithms.tree.tree import TreeNode\n\n__all__ = [\n    \"BTree\",\n    \"BTreeNode\",\n    \"DeepestLeft\",\n    \"TreeNode\",\n    \"bin_tree_to_list\",\n    \"binary_tree_paths\",\n    \"construct_tree\",\n    \"construct_tree_util\",\n    \"find_deepest_left\",\n    \"has_path_sum\",\n    \"has_path_sum2\",\n    \"has_path_sum3\",\n    \"is_balanced\",\n    \"is_same_tree\",\n    \"is_subtree\",\n    \"is_symmetric\",\n    \"is_symmetric_iterative\",\n    \"lca\",\n    \"longest_consecutive\",\n    \"max_height\",\n    \"max_path_sum\",\n    \"min_depth\",\n    \"min_height\",\n    \"path_sum\",\n    \"path_sum2\",\n    \"path_sum3\",\n    \"reverse\",\n    \"tree_print\",\n    \"left_view\",\n    \"right_view\",\n    \"top_view\",\n    \"bottom_view\",\n]\n"
  },
  {
    "path": "algorithms/tree/bin_tree_to_list.py",
    "content": "\"\"\"\nBinary Tree to Doubly Linked List\n\nConverts a binary tree to a sorted doubly linked list in-place by\nrearranging the left and right pointers of each node.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef bin_tree_to_list(root: TreeNode | None) -> TreeNode | None:\n    \"\"\"Convert a binary tree to a sorted doubly linked list.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        The head (leftmost node) of the resulting doubly linked list,\n        or None if the tree is empty.\n\n    Examples:\n        >>> bin_tree_to_list(None) is None\n        True\n    \"\"\"\n    if not root:\n        return root\n    root = _bin_tree_to_list_util(root)\n    while root.left:\n        root = root.left\n    return root\n\n\ndef _bin_tree_to_list_util(root: TreeNode | None) -> TreeNode | None:\n    \"\"\"Recursively convert subtree nodes into a doubly linked list.\n\n    Args:\n        root: The root of the subtree to convert.\n\n    Returns:\n        The root of the partially converted subtree.\n    \"\"\"\n    if not root:\n        return root\n    if root.left:\n        left = _bin_tree_to_list_util(root.left)\n        while left.right:\n            left = left.right\n        left.right = root\n        root.left = left\n    if root.right:\n        right = _bin_tree_to_list_util(root.right)\n        while right.left:\n            right = right.left\n        right.left = root\n        root.right = right\n    return root\n"
  },
  {
    "path": "algorithms/tree/binary_tree_paths.py",
    "content": "\"\"\"\nBinary Tree Paths\n\nGiven a binary tree, return all root-to-leaf paths as a list of strings\nin the format \"root->...->leaf\".\n\nReference: https://en.wikipedia.org/wiki/Binary_tree#Combinatorics\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef binary_tree_paths(root: TreeNode | None) -> list[str]:\n    \"\"\"Return all root-to-leaf paths in the binary tree.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        A list of strings representing each root-to-leaf path.\n\n    Examples:\n        >>> binary_tree_paths(None)\n        []\n    \"\"\"\n    result: list[str] = []\n    if root is None:\n        return result\n    _dfs(result, root, str(root.val))\n    return result\n\n\ndef _dfs(result: list[str], root: TreeNode, current: str) -> None:\n    \"\"\"Perform depth-first search to collect all root-to-leaf paths.\n\n    Args:\n        result: The list accumulating path strings.\n        root: The current node being visited.\n        current: The path string built so far.\n    \"\"\"\n    if root.left is None and root.right is None:\n        result.append(current)\n    if root.left:\n        _dfs(result, root.left, current + \"->\" + str(root.left.val))\n    if root.right:\n        _dfs(result, root.right, current + \"->\" + str(root.right.val))\n"
  },
  {
    "path": "algorithms/tree/binary_tree_views.py",
    "content": "\"\"\"\nBinary Tree Views\n\nCompute different \"views\" of a binary tree — the nodes visible when looking\nat the tree from a particular direction.\n\n- **Left view**: first node at each level (leftmost).\n- **Right view**: last node at each level (rightmost).\n- **Top view**: nodes visible when looking from above.\n- **Bottom view**: nodes visible when looking from below (last node at each\n  horizontal distance).\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity (all views):\n    Time:  O(n)  — each node is visited once\n    Space: O(n)  — queue / dict storage\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\nfrom algorithms.common.tree_node import TreeNode\n\n\ndef left_view(root: TreeNode | None) -> list[int]:\n    \"\"\"Return the values visible from the left side of the tree.\n\n    Args:\n        root: Root of the binary tree.\n\n    Returns:\n        List of node values, one per level, from the left.\n\n    Examples:\n        >>> from algorithms.common.tree_node import TreeNode\n        >>> root = TreeNode(1, TreeNode(2, TreeNode(4)), TreeNode(3))\n        >>> left_view(root)\n        [1, 2, 4]\n    \"\"\"\n    if root is None:\n        return []\n    result: list[int] = []\n    queue: deque[TreeNode] = deque([root])\n    while queue:\n        level_size = len(queue)\n        for i in range(level_size):\n            node = queue.popleft()\n            if i == 0:\n                result.append(node.val)\n            if node.left:\n                queue.append(node.left)\n            if node.right:\n                queue.append(node.right)\n    return result\n\n\ndef right_view(root: TreeNode | None) -> list[int]:\n    \"\"\"Return the values visible from the right side of the tree.\n\n    Args:\n        root: Root of the binary tree.\n\n    Returns:\n        List of node values, one per level, from the right.\n\n    Examples:\n        >>> from algorithms.common.tree_node import TreeNode\n        >>> root = TreeNode(1, TreeNode(2, TreeNode(4)), TreeNode(3))\n        >>> right_view(root)\n        [1, 3, 4]\n    \"\"\"\n    if root is None:\n        return []\n    result: list[int] = []\n    queue: deque[TreeNode] = deque([root])\n    while queue:\n        level_size = len(queue)\n        for i in range(level_size):\n            node = queue.popleft()\n            if i == level_size - 1:\n                result.append(node.val)\n            if node.left:\n                queue.append(node.left)\n            if node.right:\n                queue.append(node.right)\n    return result\n\n\ndef top_view(root: TreeNode | None) -> list[int]:\n    \"\"\"Return the values visible when looking at the tree from above.\n\n    Nodes are ordered by horizontal distance from root (left to right).\n\n    Args:\n        root: Root of the binary tree.\n\n    Returns:\n        List of node values visible from the top.\n\n    Examples:\n        >>> from algorithms.common.tree_node import TreeNode\n        >>> root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)),\n        ...                    TreeNode(3, None, TreeNode(6)))\n        >>> top_view(root)\n        [4, 2, 1, 3, 6]\n    \"\"\"\n    if root is None:\n        return []\n    # Map: horizontal distance -> first node value seen (BFS ensures top-most)\n    hd_map: dict[int, int] = {}\n    queue: deque[tuple[TreeNode, int]] = deque([(root, 0)])\n    while queue:\n        node, hd = queue.popleft()\n        if hd not in hd_map:\n            hd_map[hd] = node.val\n        if node.left:\n            queue.append((node.left, hd - 1))\n        if node.right:\n            queue.append((node.right, hd + 1))\n    return [hd_map[k] for k in sorted(hd_map)]\n\n\ndef bottom_view(root: TreeNode | None) -> list[int]:\n    \"\"\"Return the values visible when looking at the tree from below.\n\n    Nodes are ordered by horizontal distance from root (left to right).\n    When multiple nodes share the same horizontal distance, the last one\n    encountered in level-order (bottommost) wins.\n\n    Args:\n        root: Root of the binary tree.\n\n    Returns:\n        List of node values visible from the bottom.\n\n    Examples:\n        >>> from algorithms.common.tree_node import TreeNode\n        >>> root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)),\n        ...                    TreeNode(3, None, TreeNode(6)))\n        >>> bottom_view(root)\n        [4, 2, 5, 3, 6]\n    \"\"\"\n    if root is None:\n        return []\n    hd_map: dict[int, int] = {}\n    queue: deque[tuple[TreeNode, int]] = deque([(root, 0)])\n    while queue:\n        node, hd = queue.popleft()\n        hd_map[hd] = node.val  # overwrite → keeps bottommost\n        if node.left:\n            queue.append((node.left, hd - 1))\n        if node.right:\n            queue.append((node.right, hd + 1))\n    return [hd_map[k] for k in sorted(hd_map)]\n"
  },
  {
    "path": "algorithms/tree/bst_array_to_bst.py",
    "content": "\"\"\"\nGiven an array where elements are sorted in ascending order,\nconvert it to a height balanced BST.\n\"\"\"\n\nfrom algorithms.common.tree_node import TreeNode\n\n\ndef array_to_bst(nums):\n    if not nums:\n        return None\n    mid = len(nums) // 2\n    node = TreeNode(nums[mid])\n    node.left = array_to_bst(nums[:mid])\n    node.right = array_to_bst(nums[mid + 1 :])\n    return node\n"
  },
  {
    "path": "algorithms/tree/bst_closest_value.py",
    "content": "# Given a non-empty binary search tree and a target value,\n# find the value in the BST that is closest to the target.\n\n# Note:\n# Given target value is a floating point.\n# You are guaranteed to have only one unique value in the BST\n# that is closest to the target.\n\n\n# Definition for a binary tree node.\n# class TreeNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\n\ndef closest_value(root, target):\n    \"\"\"\n    :type root: TreeNode\n    :type target: float\n    :rtype: int\n    \"\"\"\n    a = root.val\n    kid = root.left if target < a else root.right\n    if not kid:\n        return a\n    b = closest_value(kid, target)\n    return min((a, b), key=lambda x: abs(target - x))\n"
  },
  {
    "path": "algorithms/tree/bst_count_left_node.py",
    "content": "\"\"\"\nWrite a function count_left_node returns the number of left children in the\ntree. For example: the following tree has four left children (the nodes\nstoring the values 6, 3, 7, and 10):\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    count_left_node = 4\n\n\"\"\"\n\nimport unittest\n\nfrom bst import bst\n\n\ndef count_left_node(root):\n    if root is None:\n        return 0\n    elif root.left is None:\n        return count_left_node(root.right)\n    else:\n        return 1 + count_left_node(root.left) + count_left_node(root.right)\n\n\n\"\"\"\n    The tree is created for testing:\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    count_left_node = 4\n\n\"\"\"\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.tree = bst()\n        self.tree.insert(9)\n        self.tree.insert(6)\n        self.tree.insert(12)\n        self.tree.insert(3)\n        self.tree.insert(8)\n        self.tree.insert(10)\n        self.tree.insert(15)\n        self.tree.insert(7)\n        self.tree.insert(18)\n\n    def test_count_left_node(self):\n        self.assertEqual(4, count_left_node(self.tree.root))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "algorithms/tree/bst_delete_node.py",
    "content": "\"\"\"\nGiven a root node reference of a BST and a key, delete the node\nwith the given key in the BST. Return the root node reference\n(possibly updated) of the BST.\n\nBasically, the deletion can be divided into two stages:\n\nSearch for a node to remove.\nIf the node is found, delete the node.\nNote: Time complexity should be O(height of tree).\n\nExample:\n\nroot = [5,3,6,2,4,null,7]\nkey = 3\n\n    5\n   / \\\n  3   6\n / \\\\   \\\n2   4   7\n\nGiven key to delete is 3. So we find the node with value 3 and delete it.\n\nOne valid answer is [5,4,6,2,null,null,7], shown in the following BST.\n\n    5\n   / \\\n  4   6\n /     \\\n2       7\n\nAnother valid answer is [5,2,6,null,4,null,7].\n\n    5\n   / \\\n  2   6\n   \\\\   \\\n    4   7\n\"\"\"\n\n\nclass Solution:\n    def delete_node(self, root, key):\n        \"\"\"\n        :type root: TreeNode\n        :type key: int\n        :rtype: TreeNode\n        \"\"\"\n        if not root:\n            return None\n\n        if root.val == key:\n            if root.left:\n                # Find the right most leaf of the left sub-tree\n                left_right_most = root.left\n                while left_right_most.right:\n                    left_right_most = left_right_most.right\n                # Attach right child to the right of that leaf\n                left_right_most.right = root.right\n                # Return left child instead of root, a.k.a delete root\n                return root.left\n            else:\n                return root.right\n        # If left or right child got deleted, the returned root is\n        # the child of the deleted node.\n        elif root.val > key:\n            root.left = self.deleteNode(root.left, key)\n        else:\n            root.right = self.deleteNode(root.right, key)\n        return root\n"
  },
  {
    "path": "algorithms/tree/bst_depth_sum.py",
    "content": "\"\"\"\nWrite a function depthSum returns the sum of the values stored\nin a binary search tree of integers weighted by the depth of each value.\n\nFor example:\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    depth_sum = 1*9 + 2*(6+12) + 3*(3+8+10+15) + 4*(7+18)\n\n\"\"\"\n\nimport unittest\n\nfrom bst import bst\n\n\ndef depth_sum(root, n):\n    if root:\n        return recur_depth_sum(root, 1)\n\n\ndef recur_depth_sum(root, n):\n    if root is None:\n        return 0\n    elif root.left is None and root.right is None:\n        return root.data * n\n    else:\n        return (\n            n * root.data\n            + recur_depth_sum(root.left, n + 1)\n            + recur_depth_sum(root.right, n + 1)\n        )\n\n\n\"\"\"\n    The tree is created for testing:\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    depth_sum = 1*9 + 2*(6+12) + 3*(3+8+10+15) + 4*(7+18)\n\n\"\"\"\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.tree = bst()\n        self.tree.insert(9)\n        self.tree.insert(6)\n        self.tree.insert(12)\n        self.tree.insert(3)\n        self.tree.insert(8)\n        self.tree.insert(10)\n        self.tree.insert(15)\n        self.tree.insert(7)\n        self.tree.insert(18)\n\n    def test_depth_sum(self):\n        self.assertEqual(253, depth_sum(self.tree.root, 4))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "algorithms/tree/bst_height.py",
    "content": "\"\"\"\nWrite a function height returns the height of a tree. The height is defined to\nbe the number of levels. The empty tree has height 0, a tree of one node has\nheight 1, a root node with one or two leaves as children has height 2, and so on\nFor example: height of tree is 4\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    height = 4\n\n\"\"\"\n\nimport unittest\n\nfrom bst import bst\n\n\ndef height(root):\n    if root is None:\n        return 0\n    else:\n        return 1 + max(height(root.left), height(root.right))\n\n\n\"\"\"\n    The tree is created for testing:\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    count_left_node = 4\n\n\"\"\"\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.tree = bst()\n        self.tree.insert(9)\n        self.tree.insert(6)\n        self.tree.insert(12)\n        self.tree.insert(3)\n        self.tree.insert(8)\n        self.tree.insert(10)\n        self.tree.insert(15)\n        self.tree.insert(7)\n        self.tree.insert(18)\n\n    def test_height(self):\n        self.assertEqual(4, height(self.tree.root))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "algorithms/tree/bst_is_bst.py",
    "content": "\"\"\"\nGiven a binary tree, determine if it is a valid binary search tree (BST).\n\nAssume a BST is defined as follows:\n\nThe left subtree of a node contains only nodes\nwith keys less than the node's key.\nThe right subtree of a node contains only nodes\nwith keys greater than the node's key.\nBoth the left and right subtrees must also be binary search trees.\nExample 1:\n    2\n   / \\\n  1   3\nBinary tree [2,1,3], return true.\nExample 2:\n    1\n   / \\\n  2   3\nBinary tree [1,2,3], return false.\n\"\"\"\n\n\ndef is_bst(root):\n    \"\"\"\n    :type root: TreeNode\n    :rtype: bool\n    \"\"\"\n\n    stack = []\n    pre = None\n\n    while root or stack:\n        while root:\n            stack.append(root)\n            root = root.left\n        root = stack.pop()\n        if pre and root.val <= pre.val:\n            return False\n        pre = root\n        root = root.right\n\n    return True\n"
  },
  {
    "path": "algorithms/tree/bst_iterator.py",
    "content": "class BSTIterator:\n    def __init__(self, root):\n        self.stack = []\n        while root:\n            self.stack.append(root)\n            root = root.left\n\n    def has_next(self):\n        return bool(self.stack)\n\n    def next(self):\n        node = self.stack.pop()\n        tmp = node\n        if tmp.right:\n            tmp = tmp.right\n            while tmp:\n                self.stack.append(tmp)\n                tmp = tmp.left\n        return node.val\n"
  },
  {
    "path": "algorithms/tree/bst_kth_smallest.py",
    "content": "class Node:\n    def __init__(self, val, left=None, right=None):\n        self.val = val\n        self.left = left\n        self.right = right\n\n\ndef kth_smallest(root, k):\n    stack = []\n    while root or stack:\n        while root:\n            stack.append(root)\n            root = root.left\n        root = stack.pop()\n        k -= 1\n        if k == 0:\n            break\n        root = root.right\n    return root.val\n\n\nclass Solution:\n    def kth_smallest(self, root, k):\n        \"\"\"\n        :type root: TreeNode\n        :type k: int\n        :rtype: int\n        \"\"\"\n        count = []\n        self.helper(root, count)\n        return count[k - 1]\n\n    def helper(self, node, count):\n        if not node:\n            return\n\n        self.helper(node.left, count)\n        count.append(node.val)\n        self.helper(node.right, count)\n\n\nif __name__ == \"__main__\":\n    n1 = Node(100)\n    n2 = Node(50)\n    n3 = Node(150)\n    n4 = Node(25)\n    n5 = Node(75)\n    n6 = Node(125)\n    n7 = Node(175)\n    n1.left, n1.right = n2, n3\n    n2.left, n2.right = n4, n5\n    n3.left, n3.right = n6, n7\n    print(kth_smallest(n1, 2))\n    print(Solution().kth_smallest(n1, 2))\n"
  },
  {
    "path": "algorithms/tree/bst_lowest_common_ancestor.py",
    "content": "\"\"\"\nGiven a binary search tree (BST),\nfind the lowest common ancestor (LCA) of two given nodes in the BST.\n\nAccording to the definition of LCA on Wikipedia:\n    “The lowest common ancestor is defined between two\n    nodes v and w as the lowest node in T that has both v and w\n    as descendants (where we allow a node to be a descendant of itself).”\n\n        _______6______\n       /              \\\n    ___2__          ___8__\n   /      \\\\        /      \\\n   0      _4       7       9\n         /  \\\n         3   5\n\nFor example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6.\nAnother example is LCA of nodes 2 and 4 is 2,\nsince a node can be a descendant of itself according to the LCA definition.\n\"\"\"\n\n\ndef lowest_common_ancestor(root, p, q):\n    \"\"\"\n    :type root: Node\n    :type p: Node\n    :type q: Node\n    :rtype: Node\n    \"\"\"\n    while root:\n        if p.val > root.val < q.val:\n            root = root.right\n        elif p.val < root.val > q.val:\n            root = root.left\n        else:\n            return root\n"
  },
  {
    "path": "algorithms/tree/bst_num_empty.py",
    "content": "\"\"\"\nWrite a function num_empty returns returns the number of empty branches in a\ntree. Function should count the total number of empty branches among the nodes\nof the tree. A leaf node has two empty branches. In the case, if root is None,\nit considered as a 1 empty branch\nFor example: the following tree has 10 empty branch (* is empty branch)\n\n                    9 __\n                 /      \\\\___\n               6            12\n              / \\\\          /   \\\n            3     8       10      15\n          /  \\\\   / \\\\     /  \\\\    /   \\\n         *    * 7   *   *    *  *    18\n               / \\\\                   /  \\\n              *   *                 *    *\n\n    empty_branch = 10\n\n\"\"\"\n\nimport unittest\n\nfrom bst import bst\n\n\ndef num_empty(root):\n    if root is None:\n        return 1\n    elif root.left is None and root.right:\n        return 1 + num_empty(root.right)\n    elif root.right is None and root.left:\n        return 1 + num_empty(root.left)\n    else:\n        return num_empty(root.left) + num_empty(root.right)\n\n\n\"\"\"\n    The tree is created for testing:\n\n                    9\n                 /      \\\n               6         12\n              / \\\\       /   \\\n            3     8   10      15\n                 /              \\\n                7                18\n\n    num_empty = 10\n\n\"\"\"\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.tree = bst()\n        self.tree.insert(9)\n        self.tree.insert(6)\n        self.tree.insert(12)\n        self.tree.insert(3)\n        self.tree.insert(8)\n        self.tree.insert(10)\n        self.tree.insert(15)\n        self.tree.insert(7)\n        self.tree.insert(18)\n\n    def test_num_empty(self):\n        self.assertEqual(10, num_empty(self.tree.root))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "algorithms/tree/bst_predecessor.py",
    "content": "def predecessor(root, node):\n    pred = None\n    while root:\n        if node.val > root.val:\n            pred = root\n            root = root.right\n        else:\n            root = root.left\n    return pred\n"
  },
  {
    "path": "algorithms/tree/bst_serialize_deserialize.py",
    "content": "from algorithms.common.tree_node import TreeNode\n\n\ndef serialize(root):\n    def build_string(node):\n        if node:\n            vals.append(str(node.val))\n            build_string(node.left)\n            build_string(node.right)\n        else:\n            vals.append(\"#\")\n\n    vals = []\n    build_string(root)\n    return \" \".join(vals)\n\n\ndef deserialize(data):\n    def build_tree():\n        val = next(vals)\n        if val == \"#\":\n            return None\n        node = TreeNode(int(val))\n        node.left = build_tree()\n        node.right = build_tree()\n        return node\n\n    vals = iter(data.split())\n    return build_tree()\n"
  },
  {
    "path": "algorithms/tree/bst_successor.py",
    "content": "def successor(root, node):\n    succ = None\n    while root:\n        if node.val < root.val:\n            succ = root\n            root = root.left\n        else:\n            root = root.right\n    return succ\n"
  },
  {
    "path": "algorithms/tree/bst_unique_bst.py",
    "content": "\"\"\"\nGiven n, how many structurally unique BST's\n(binary search trees) that store values 1...n?\n\nFor example,\nGiven n = 3, there are a total of 5 unique BST's.\n\n   1         3     3      2      1\n    \\\\       /     /      / \\\\      \\\n     3     2     1      1   3      2\n    /     /       \\\\                 \\\n   2     1         2                 3\n\"\"\"\n\n\"\"\"\nTaking 1~n as root respectively:\n1 as root: # of trees = F(0) * F(n-1)  // F(0) == 1\n2 as root: # of trees = F(1) * F(n-2)\n3 as root: # of trees = F(2) * F(n-3)\n...\nn-1 as root: # of trees = F(n-2) * F(1)\nn as root:   # of trees = F(n-1) * F(0)\n\nSo, the formulation is:\nF(n) = F(0) * F(n-1) + F(1) * F(n-2) + F(2) * F(n-3)\n       + ... + F(n-2) * F(1) + F(n-1) * F(0)\n\"\"\"\n\n\ndef num_trees(n):\n    \"\"\"\n    :type n: int\n    :rtype: int\n    \"\"\"\n    dp = [0] * (n + 1)\n    dp[0] = 1\n    dp[1] = 1\n    for i in range(2, n + 1):\n        for j in range(i + 1):\n            dp[i] += dp[i - j] * dp[j - 1]\n    return dp[-1]\n"
  },
  {
    "path": "algorithms/tree/bst_validate_bst.py",
    "content": "# ===============================================================================\n# Validate Binary Search Tree\n\"\"\"\nTo check if the given binary tree is a valid binary search\ntree (BST), we need to ensure that:\n    1. The left subtree of a node contains only nodes with\n       keys less than the node's key.\n    2. The right subtree of a node contains only nodes with\n       keys greater than the node's key.\n    3. Both the left and right subtrees must also be binary\n       search trees.\n\"\"\"\n# ===============================================================================\n\nfrom algorithms.common.tree_node import TreeNode\n\n\n# Function to validate if a binary tree is a BST\ndef validate_bst(node):\n    \"\"\"\n    Validate if a binary tree is a binary search tree (BST).\n    Input params : Tree Node to be validated\n    Returns : Tuple (\n        is_bst: bool,\n        min_value: int | None,\n        max_value: int | None\n    )\n    \"\"\"\n\n    # Base case: An empty tree is a valid BST\n    if not node:\n        return (True, None, None)\n\n    # Validate the left and right subtrees\n    valid_left, minn_left, maxx_left = validate_bst(node.left)\n    valid_right, minn_right, maxx_right = validate_bst(node.right)\n\n    # If either subtree is not valid, the whole tree is not a valid BST\n    if not valid_left or not valid_right:\n        return (\n            False,\n            minn_left if minn_left is not None else node.val,\n            maxx_right if maxx_right is not None else node.val,\n        )\n\n    # Check the current node's value against the max of the left subtree\n    if maxx_left is not None and maxx_left > node.val:\n        return (\n            False,\n            minn_left if minn_left is not None else node.val,\n            maxx_right if maxx_right is not None else node.val,\n        )\n\n    # Check the current node's value against the min of the right subtree\n    if minn_right is not None and minn_right < node.val:\n        return (\n            False,\n            minn_left if minn_left is not None else node.val,\n            maxx_right if maxx_right is not None else node.val,\n        )\n\n    # If all checks pass, the tree/subtree is a valid BST\n    return (\n        True,\n        minn_left if minn_left is not None else node.val,\n        maxx_right if maxx_right is not None else node.val,\n    )\n\n\n# Example usage\nif __name__ == \"__main__\":\n    # Constructing a simple binary tree\n    root = TreeNode(10)\n    root.left = TreeNode(5)\n    root.right = TreeNode(15)\n    root.right.left = TreeNode(12)\n    root.right.right = TreeNode(20)\n\n    \"\"\"\n          10\n         /  \\\n        5    15\n            /  \\\n           12   20\n    \"\"\"\n\n    # Validate if the constructed tree is a BST\n    is_bst, _, _ = validate_bst(root)\n    print(f\"The tree is a valid BST: {is_bst}\")\n"
  },
  {
    "path": "algorithms/tree/construct_tree_postorder_preorder.py",
    "content": "\"\"\"\nConstruct Tree from Preorder and Postorder Traversal\n\nGiven preorder and postorder traversals of a full binary tree, construct the\ntree and return its inorder traversal. A full binary tree has either zero or\ntwo children per node.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree#Types_of_binary_trees\n\nComplexity:\n    Time:  O(n^2) due to linear search in postorder array\n    Space: O(n) for the constructed tree\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.common.tree_node import TreeNode\n\npre_index = 0\n\n\ndef construct_tree_util(\n    pre: list[int], post: list[int], low: int, high: int, size: int\n) -> TreeNode | None:\n    \"\"\"Recursively construct a binary tree from preorder and postorder arrays.\n\n    Uses a global pre_index to track the current position in the preorder\n    array during recursive construction.\n\n    Args:\n        pre: The preorder traversal array.\n        post: The postorder traversal array.\n        low: The lower bound index in the postorder array.\n        high: The upper bound index in the postorder array.\n        size: The total number of elements.\n\n    Returns:\n        The root of the constructed subtree, or None if bounds are invalid.\n\n    Examples:\n        >>> construct_tree_util([1, 2, 3], [2, 3, 1], 0, 2, 3) is not None\n        True\n    \"\"\"\n    global pre_index\n\n    if pre_index == -1:\n        pre_index = 0\n\n    if pre_index >= size or low > high:\n        return None\n\n    root = TreeNode(pre[pre_index])\n    pre_index += 1\n\n    if low == high or pre_index >= size:\n        return root\n\n    i = low\n    while i <= high:\n        if pre[pre_index] == post[i]:\n            break\n        i += 1\n\n    if i <= high:\n        root.left = construct_tree_util(pre, post, low, i, size)\n        root.right = construct_tree_util(pre, post, i + 1, high, size)\n\n    return root\n\n\ndef construct_tree(pre: list[int], post: list[int], size: int) -> list[int]:\n    \"\"\"Construct a full binary tree and return its inorder traversal.\n\n    Args:\n        pre: The preorder traversal array.\n        post: The postorder traversal array.\n        size: The number of elements.\n\n    Returns:\n        A list of values representing the inorder traversal of the\n        constructed tree.\n\n    Examples:\n        >>> construct_tree([1, 2, 4, 5, 3, 6, 7], [4, 5, 2, 6, 7, 3, 1], 7)\n        [4, 2, 5, 1, 6, 3, 7]\n    \"\"\"\n    root = construct_tree_util(pre, post, 0, size - 1, size)\n    return _inorder(root)\n\n\ndef _inorder(root: TreeNode | None, result: list[int] | None = None) -> list[int]:\n    \"\"\"Return the inorder traversal of a binary tree.\n\n    Args:\n        root: The root of the tree to traverse.\n        result: Accumulator list for the traversal values.\n\n    Returns:\n        A list of node values in inorder sequence.\n    \"\"\"\n    if root is None:\n        return []\n    if result is None:\n        result = []\n\n    _inorder(root.left, result)\n    result.append(root.val)\n    _inorder(root.right, result)\n    return result\n"
  },
  {
    "path": "algorithms/tree/deepest_left.py",
    "content": "\"\"\"\nDeepest Left Leaf\n\nGiven a binary tree, find the deepest node that is the left child of its\nparent node.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\nclass DeepestLeft:\n    \"\"\"Container to track the deepest left node found during traversal.\n\n    Examples:\n        >>> dl = DeepestLeft()\n        >>> dl.depth\n        0\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.depth: int = 0\n        self.Node: TreeNode | None = None\n\n\ndef find_deepest_left(\n    root: TreeNode | None, is_left: bool, depth: int, res: DeepestLeft\n) -> None:\n    \"\"\"Recursively find the deepest left child in a binary tree.\n\n    Args:\n        root: The current node being examined.\n        is_left: Whether the current node is a left child.\n        depth: The current depth in the tree.\n        res: A DeepestLeft instance tracking the best result so far.\n\n    Examples:\n        >>> res = DeepestLeft()\n        >>> find_deepest_left(None, True, 1, res)\n    \"\"\"\n    if not root:\n        return\n    if is_left and depth > res.depth:\n        res.depth = depth\n        res.Node = root\n    find_deepest_left(root.left, True, depth + 1, res)\n    find_deepest_left(root.right, False, depth + 1, res)\n"
  },
  {
    "path": "algorithms/tree/invert_tree.py",
    "content": "\"\"\"\nInvert Binary Tree\n\nInverts a binary tree by swapping the left and right children of every node.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef reverse(root: TreeNode | None) -> None:\n    \"\"\"Invert a binary tree in-place by swapping left and right children.\n\n    Args:\n        root: The root of the binary tree to invert.\n\n    Examples:\n        >>> reverse(None)\n    \"\"\"\n    if root is None:\n        return\n    root.left, root.right = root.right, root.left\n    if root.left:\n        reverse(root.left)\n    if root.right:\n        reverse(root.right)\n"
  },
  {
    "path": "algorithms/tree/is_balanced.py",
    "content": "\"\"\"\nBalanced Binary Tree\n\nDetermines whether a binary tree is height-balanced, meaning the depth of the\nleft and right subtrees of every node differ by at most one.\n\nReference: https://en.wikipedia.org/wiki/AVL_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef is_balanced(root: TreeNode | None) -> bool:\n    \"\"\"Check whether a binary tree is height-balanced.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        True if the tree is balanced, False otherwise.\n\n    Examples:\n        >>> is_balanced(None)\n        True\n    \"\"\"\n    return _get_depth(root) != -1\n\n\ndef _get_depth(root: TreeNode | None) -> int:\n    \"\"\"Compute the depth of a tree, returning -1 if unbalanced.\n\n    Args:\n        root: The root of the subtree.\n\n    Returns:\n        The depth of the subtree, or -1 if it is unbalanced.\n    \"\"\"\n    if root is None:\n        return 0\n    left = _get_depth(root.left)\n    right = _get_depth(root.right)\n    if abs(left - right) > 1 or -1 in [left, right]:\n        return -1\n    return 1 + max(left, right)\n"
  },
  {
    "path": "algorithms/tree/is_subtree.py",
    "content": "\"\"\"\nSubtree Check\n\nGiven two binary trees s and t, check whether t is a subtree of s. A subtree\nof s is a tree consisting of a node in s and all of its descendants.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(m * n) where m and n are sizes of the two trees\n    Space: O(m) due to BFS queue\n\"\"\"\n\nfrom __future__ import annotations\n\nimport collections\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef is_subtree(big: TreeNode, small: TreeNode) -> bool:\n    \"\"\"Check whether the small tree is a subtree of the big tree.\n\n    Args:\n        big: The root of the larger tree.\n        small: The root of the potential subtree.\n\n    Returns:\n        True if small is a subtree of big, False otherwise.\n\n    Examples:\n        >>> node = TreeNode(1)\n        >>> is_subtree(node, node)\n        True\n    \"\"\"\n    flag = False\n    queue: collections.deque[TreeNode] = collections.deque()\n    queue.append(big)\n    while queue:\n        node = queue.popleft()\n        if node.val == small.val:\n            flag = _comp(node, small)\n            break\n        else:\n            queue.append(node.left)\n            queue.append(node.right)\n    return flag\n\n\ndef _comp(p: TreeNode | None, q: TreeNode | None) -> bool:\n    \"\"\"Recursively compare two trees for structural and value equality.\n\n    Args:\n        p: A node from the first tree.\n        q: A node from the second tree.\n\n    Returns:\n        True if both subtrees are identical, False otherwise.\n    \"\"\"\n    if p is None and q is None:\n        return True\n    if p is not None and q is not None:\n        return p.val == q.val and _comp(p.left, q.left) and _comp(p.right, q.right)\n    return False\n"
  },
  {
    "path": "algorithms/tree/is_symmetric.py",
    "content": "\"\"\"\nSymmetric Tree\n\nGiven a binary tree, check whether it is a mirror of itself (i.e., symmetric\naround its center). Provides both recursive and iterative solutions.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef is_symmetric(root: TreeNode | None) -> bool:\n    \"\"\"Check whether a binary tree is symmetric using recursion.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        True if the tree is symmetric, False otherwise.\n\n    Examples:\n        >>> is_symmetric(None)\n        True\n    \"\"\"\n    if root is None:\n        return True\n    return _helper(root.left, root.right)\n\n\ndef _helper(p: TreeNode | None, q: TreeNode | None) -> bool:\n    \"\"\"Recursively check whether two subtrees are mirrors of each other.\n\n    Args:\n        p: The root of the left subtree.\n        q: The root of the right subtree.\n\n    Returns:\n        True if the subtrees are mirror images, False otherwise.\n    \"\"\"\n    if p is None and q is None:\n        return True\n    if p is not None or q is not None or q.val != p.val:\n        return False\n    return _helper(p.left, q.right) and _helper(p.right, q.left)\n\n\ndef is_symmetric_iterative(root: TreeNode | None) -> bool:\n    \"\"\"Check whether a binary tree is symmetric using iteration.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        True if the tree is symmetric, False otherwise.\n\n    Examples:\n        >>> is_symmetric_iterative(None)\n        True\n    \"\"\"\n    if root is None:\n        return True\n    stack: list[list[TreeNode | None]] = [[root.left, root.right]]\n    while stack:\n        left, right = stack.pop()\n        if left is None and right is None:\n            continue\n        if left is None or right is None:\n            return False\n        if left.val == right.val:\n            stack.append([left.left, right.right])\n            stack.append([left.right, right.left])\n        else:\n            return False\n    return True\n"
  },
  {
    "path": "algorithms/tree/longest_consecutive.py",
    "content": "\"\"\"\nLongest Consecutive Sequence in Binary Tree\n\nGiven a binary tree, find the length of the longest consecutive sequence path\nfrom parent to child (values increasing by one).\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef longest_consecutive(root: TreeNode | None) -> int:\n    \"\"\"Find the length of the longest parent-to-child consecutive sequence.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        The length of the longest consecutive sequence path.\n\n    Examples:\n        >>> longest_consecutive(None)\n        0\n    \"\"\"\n    if root is None:\n        return 0\n    max_len = 0\n    _dfs(root, 0, root.val, max_len)\n    return max_len\n\n\ndef _dfs(root: TreeNode | None, current: int, target: int, max_len: int) -> None:\n    \"\"\"Recursively search for the longest consecutive sequence.\n\n    Args:\n        root: The current node being visited.\n        current: The current consecutive sequence length.\n        target: The expected value for the current node to continue the sequence.\n        max_len: The maximum sequence length found so far.\n    \"\"\"\n    if root is None:\n        return\n    if root.val == target:\n        current += 1\n    else:\n        current = 1\n    max_len = max(current, max_len)\n    _dfs(root.left, current, root.val + 1, max_len)\n    _dfs(root.right, current, root.val + 1, max_len)\n"
  },
  {
    "path": "algorithms/tree/lowest_common_ancestor.py",
    "content": "\"\"\"\nLowest Common Ancestor\n\nGiven a binary tree, find the lowest common ancestor (LCA) of two given nodes.\nThe LCA is the lowest node that has both nodes as descendants (a node can be a\ndescendant of itself).\n\nReference: https://en.wikipedia.org/wiki/Lowest_common_ancestor\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef lca(root: TreeNode | None, p: TreeNode, q: TreeNode) -> TreeNode | None:\n    \"\"\"Find the lowest common ancestor of two nodes in a binary tree.\n\n    Args:\n        root: The root of the binary tree.\n        p: The first target node.\n        q: The second target node.\n\n    Returns:\n        The lowest common ancestor node, or None if not found.\n\n    Examples:\n        >>> node = TreeNode(1)\n        >>> lca(node, node, node).val\n        1\n    \"\"\"\n    if root is None or root is p or root is q:\n        return root\n    left = lca(root.left, p, q)\n    right = lca(root.right, p, q)\n    if left is not None and right is not None:\n        return root\n    return left if left else right\n"
  },
  {
    "path": "algorithms/tree/max_height.py",
    "content": "\"\"\"\nMaximum Depth of Binary Tree\n\nGiven a binary tree, find its maximum depth. The maximum depth is the number\nof nodes along the longest path from the root down to the farthest leaf.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef max_height(root: TreeNode | None) -> int:\n    \"\"\"Compute the maximum depth of a binary tree using iterative BFS.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        The maximum depth (number of levels) of the tree.\n\n    Examples:\n        >>> max_height(None)\n        0\n    \"\"\"\n    if root is None:\n        return 0\n    height = 0\n    queue: deque[TreeNode] = deque([root])\n    while queue:\n        height += 1\n        level: deque[TreeNode] = deque()\n        while queue:\n            node = queue.popleft()\n            if node.left is not None:\n                level.append(node.left)\n            if node.right is not None:\n                level.append(node.right)\n        queue = level\n    return height\n"
  },
  {
    "path": "algorithms/tree/max_path_sum.py",
    "content": "\"\"\"\nBinary Tree Maximum Path Sum\n\nGiven a binary tree, find the maximum path sum. A path is any sequence of nodes\nfrom some starting node to any node in the tree along parent-child connections.\nThe path must contain at least one node.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n) due to recursion stack\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef max_path_sum(root: TreeNode | None) -> float:\n    \"\"\"Find the maximum path sum in a binary tree.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        The maximum sum of any path through the tree.\n\n    Examples:\n        >>> max_path_sum(TreeNode(1))\n        1\n    \"\"\"\n    maximum = float(\"-inf\")\n    _helper(root, maximum)\n    return maximum\n\n\ndef _helper(root: TreeNode | None, maximum: float) -> float:\n    \"\"\"Recursively compute the maximum single-branch sum from each node.\n\n    Args:\n        root: The current node.\n        maximum: The running maximum path sum.\n\n    Returns:\n        The maximum sum of a path extending from this node to a descendant.\n    \"\"\"\n    if root is None:\n        return 0\n    left = _helper(root.left, maximum)\n    right = _helper(root.right, maximum)\n    maximum = max(maximum, left + right + root.val)\n    return root.val + maximum\n"
  },
  {
    "path": "algorithms/tree/min_height.py",
    "content": "\"\"\"\nMinimum Depth of Binary Tree\n\nGiven a binary tree, find its minimum depth. The minimum depth is the number\nof nodes along the shortest path from the root down to the nearest leaf.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef min_depth(self: object, root: TreeNode | None) -> int:\n    \"\"\"Compute the minimum depth of a binary tree using recursion.\n\n    Args:\n        self: Unused parameter (kept for backward compatibility).\n        root: The root of the binary tree.\n\n    Returns:\n        The minimum depth of the tree.\n    \"\"\"\n    if root is None:\n        return 0\n    if root.left is not None or root.right is not None:\n        return max(self.minDepth(root.left), self.minDepth(root.right)) + 1\n    return min(self.minDepth(root.left), self.minDepth(root.right)) + 1\n\n\ndef min_height(root: TreeNode | None) -> int:\n    \"\"\"Compute the minimum depth of a binary tree using iterative BFS.\n\n    Args:\n        root: The root of the binary tree.\n\n    Returns:\n        The minimum depth (number of levels to nearest leaf) of the tree.\n\n    Examples:\n        >>> min_height(None)\n        0\n    \"\"\"\n    if root is None:\n        return 0\n    height = 0\n    level: list[TreeNode] = [root]\n    while level:\n        height += 1\n        new_level: list[TreeNode] = []\n        for node in level:\n            if node.left is None and node.right is None:\n                return height\n            if node.left is not None:\n                new_level.append(node.left)\n            if node.right is not None:\n                new_level.append(node.right)\n        level = new_level\n    return height\n"
  },
  {
    "path": "algorithms/tree/path_sum.py",
    "content": "\"\"\"\nPath Sum\n\nGiven a binary tree and a target sum, determine if the tree has a root-to-leaf\npath such that adding up all values along the path equals the given sum.\nProvides recursive, DFS, and BFS solutions.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef has_path_sum(root: TreeNode | None, sum: int) -> bool:\n    \"\"\"Check if a root-to-leaf path with the given sum exists (recursive).\n\n    Args:\n        root: The root of the binary tree.\n        sum: The target sum.\n\n    Returns:\n        True if such a path exists, False otherwise.\n\n    Examples:\n        >>> has_path_sum(None, 0)\n        False\n    \"\"\"\n    if root is None:\n        return False\n    if root.left is None and root.right is None and root.val == sum:\n        return True\n    sum -= root.val\n    return has_path_sum(root.left, sum) or has_path_sum(root.right, sum)\n\n\ndef has_path_sum2(root: TreeNode | None, sum: int) -> bool:\n    \"\"\"Check if a root-to-leaf path with the given sum exists (DFS with stack).\n\n    Args:\n        root: The root of the binary tree.\n        sum: The target sum.\n\n    Returns:\n        True if such a path exists, False otherwise.\n\n    Examples:\n        >>> has_path_sum2(None, 0)\n        False\n    \"\"\"\n    if root is None:\n        return False\n    stack: list[tuple[TreeNode, int]] = [(root, root.val)]\n    while stack:\n        node, val = stack.pop()\n        if node.left is None and node.right is None and val == sum:\n            return True\n        if node.left is not None:\n            stack.append((node.left, val + node.left.val))\n        if node.right is not None:\n            stack.append((node.right, val + node.right.val))\n    return False\n\n\ndef has_path_sum3(root: TreeNode | None, sum: int) -> bool:\n    \"\"\"Check if a root-to-leaf path with the given sum exists (BFS with queue).\n\n    Args:\n        root: The root of the binary tree.\n        sum: The target sum.\n\n    Returns:\n        True if such a path exists, False otherwise.\n\n    Examples:\n        >>> has_path_sum3(None, 0)\n        False\n    \"\"\"\n    if root is None:\n        return False\n    queue: deque[tuple[TreeNode, int]] = deque([(root, sum - root.val)])\n    while queue:\n        node, val = queue.popleft()\n        if node.left is None and node.right is None and val == 0:\n            return True\n        if node.left is not None:\n            queue.append((node.left, val - node.left.val))\n        if node.right is not None:\n            queue.append((node.right, val - node.right.val))\n    return False\n"
  },
  {
    "path": "algorithms/tree/path_sum2.py",
    "content": "\"\"\"\nPath Sum II\n\nGiven a binary tree and a target sum, find all root-to-leaf paths where each\npath's sum equals the given sum. Provides recursive, DFS, and BFS solutions.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(n)\n    Space: O(n)\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections import deque\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef path_sum(root: TreeNode | None, sum: int) -> list[list[int]]:\n    \"\"\"Find all root-to-leaf paths with the given sum (recursive DFS).\n\n    Args:\n        root: The root of the binary tree.\n        sum: The target sum.\n\n    Returns:\n        A list of paths, where each path is a list of node values.\n\n    Examples:\n        >>> path_sum(None, 0)\n        []\n    \"\"\"\n    if root is None:\n        return []\n    result: list[list[int]] = []\n    _dfs(root, sum, [], result)\n    return result\n\n\ndef _dfs(root: TreeNode, sum: int, path: list[int], result: list[list[int]]) -> None:\n    \"\"\"Recursively collect paths that sum to the target value.\n\n    Args:\n        root: The current node.\n        sum: The remaining target sum.\n        path: The path accumulated so far.\n        result: The list accumulating valid paths.\n    \"\"\"\n    if root.left is None and root.right is None and root.val == sum:\n        path.append(root.val)\n        result.append(path)\n    if root.left is not None:\n        _dfs(root.left, sum - root.val, path + [root.val], result)\n    if root.right is not None:\n        _dfs(root.right, sum - root.val, path + [root.val], result)\n\n\ndef path_sum2(root: TreeNode | None, target: int) -> list[list[int]]:\n    \"\"\"Find all root-to-leaf paths with the given sum (DFS with stack).\n\n    Args:\n        root: The root of the binary tree.\n        target: The target sum.\n\n    Returns:\n        A list of paths, where each path is a list of node values.\n\n    Examples:\n        >>> path_sum2(None, 0)\n        []\n    \"\"\"\n    if root is None:\n        return []\n    result: list[list[int]] = []\n    stack: list[tuple[TreeNode, list[int]]] = [(root, [root.val])]\n    while stack:\n        node, path = stack.pop()\n        if node.left is None and node.right is None and sum(path) == target:\n            result.append(path)\n        if node.left is not None:\n            stack.append((node.left, path + [node.left.val]))\n        if node.right is not None:\n            stack.append((node.right, path + [node.right.val]))\n    return result\n\n\ndef path_sum3(root: TreeNode | None, sum: int) -> list[list[int]]:\n    \"\"\"Find all root-to-leaf paths with the given sum (BFS with queue).\n\n    Args:\n        root: The root of the binary tree.\n        sum: The target sum.\n\n    Returns:\n        A list of paths, where each path is a list of node values.\n\n    Examples:\n        >>> path_sum3(None, 0)\n        []\n    \"\"\"\n    if root is None:\n        return []\n    result: list[list[int]] = []\n    initial = (root, root.val, [root.val])\n    queue: deque[tuple[TreeNode, int, list[int]]] = deque([initial])\n    while queue:\n        node, val, path = queue.popleft()\n        if node.left is None and node.right is None and val == sum:\n            result.append(path)\n        if node.left is not None:\n            queue.append((node.left, val + node.left.val, path + [node.left.val]))\n        if node.right is not None:\n            queue.append((node.right, val + node.right.val, path + [node.right.val]))\n    return result\n"
  },
  {
    "path": "algorithms/tree/pretty_print.py",
    "content": "\"\"\"\nPretty Print Tree\n\nPrints a dictionary-based tree structure in a human-readable format showing\nkeys and their nested elements.\n\nReference: https://en.wikipedia.org/wiki/Tree_(data_structure)\n\nComplexity:\n    Time:  O(n) where n is total number of elements\n    Space: O(1) additional space\n\"\"\"\n\nfrom __future__ import annotations\n\n\ndef tree_print(tree: dict) -> list[str]:\n    \"\"\"Format a dictionary tree as a list of readable strings.\n\n    Each top-level key maps to a list of sub-elements. The output\n    represents each key followed by its sub-elements joined with arrows.\n\n    Args:\n        tree: A dictionary mapping keys to lists of sub-elements.\n\n    Returns:\n        A list of formatted strings representing each tree branch.\n\n    Examples:\n        >>> tree_print({\"a\": [\"Adam\", \"Book\", 4]})\n        ['a -> Adam -> Book -> 4']\n    \"\"\"\n    lines: list[str] = []\n    for key in tree:\n        parts = [str(key)]\n        tree_element = tree[key]\n        for sub_elem in tree_element:\n            parts.append(str(sub_elem))\n        lines.append(\" -> \".join(parts))\n    return lines\n"
  },
  {
    "path": "algorithms/tree/same_tree.py",
    "content": "\"\"\"\nSame Tree\n\nGiven two binary trees, check if they are structurally identical and the\nnodes have the same values.\n\nReference: https://en.wikipedia.org/wiki/Binary_tree\n\nComplexity:\n    Time:  O(min(n, m)) where n and m are sizes of the trees\n    Space: O(min(h1, h2)) where h1 and h2 are the heights\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom algorithms.tree.tree import TreeNode\n\n\ndef is_same_tree(tree_p: TreeNode | None, tree_q: TreeNode | None) -> bool:\n    \"\"\"Check whether two binary trees are identical.\n\n    Args:\n        tree_p: The root of the first binary tree.\n        tree_q: The root of the second binary tree.\n\n    Returns:\n        True if both trees are structurally identical with equal node\n        values, False otherwise.\n\n    Examples:\n        >>> is_same_tree(None, None)\n        True\n    \"\"\"\n    if tree_p is None and tree_q is None:\n        return True\n    if tree_p is not None and tree_q is not None and tree_p.val == tree_q.val:\n        return is_same_tree(tree_p.left, tree_q.left) and is_same_tree(\n            tree_p.right, tree_q.right\n        )\n    return False\n"
  },
  {
    "path": "algorithms/tree/traversal_inorder.py",
    "content": "\"\"\"\nTime complexity : O(n)\n\"\"\"\n\n\nclass Node:\n    def __init__(self, val, left=None, right=None):\n        self.val = val\n        self.left = left\n        self.right = right\n\n\ndef inorder(root):\n    \"\"\"In order function\"\"\"\n    res = []\n    if not root:\n        return res\n    stack = []\n    while root or stack:\n        while root:\n            stack.append(root)\n            root = root.left\n        root = stack.pop()\n        res.append(root.val)\n        root = root.right\n    return res\n\n\ndef inorder_rec(root, res=None):\n    \"\"\"Recursive Implementation\"\"\"\n    if root is None:\n        return []\n    if res is None:\n        res = []\n    inorder_rec(root.left, res)\n    res.append(root.val)\n    inorder_rec(root.right, res)\n    return res\n\n\nif __name__ == \"__main__\":\n    n1 = Node(100)\n    n2 = Node(50)\n    n3 = Node(150)\n    n4 = Node(25)\n    n5 = Node(75)\n    n6 = Node(125)\n    n7 = Node(175)\n    n1.left, n1.right = n2, n3\n    n2.left, n2.right = n4, n5\n    n3.left, n3.right = n6, n7\n\n    assert inorder(n1) == [25, 50, 75, 100, 125, 150, 175]\n    assert inorder_rec(n1) == [25, 50, 75, 100, 125, 150, 175]\n"
  },
  {
    "path": "algorithms/tree/traversal_level_order.py",
    "content": "\"\"\"\nGiven a binary tree, return the level order traversal of\nits nodes' values. (ie, from left to right, level by level).\n\nFor example:\nGiven binary tree [3,9,20,null,null,15,7],\n    3\n   / \\\n  9  20\n    /  \\\n   15   7\nreturn its level order traversal as:\n[\n  [3],\n  [9,20],\n  [15,7]\n]\n\"\"\"\n\n\ndef level_order(root):\n    ans = []\n    if not root:\n        return ans\n    level = [root]\n    while level:\n        current = []\n        new_level = []\n        for node in level:\n            current.append(node.val)\n            if node.left:\n                new_level.append(node.left)\n            if node.right:\n                new_level.append(node.right)\n        level = new_level\n        ans.append(current)\n    return ans\n"
  },
  {
    "path": "algorithms/tree/traversal_postorder.py",
    "content": "\"\"\"\nTime complexity : O(n)\n\"\"\"\n\n\nclass Node:\n    def __init__(self, val, left=None, right=None):\n        self.val = val\n        self.left = left\n        self.right = right\n\n\ndef postorder(root):\n    res_temp = []\n    res = []\n    if not root:\n        return res\n    stack = []\n    stack.append(root)\n    while stack:\n        root = stack.pop()\n        res_temp.append(root.val)\n        if root.left:\n            stack.append(root.left)\n        if root.right:\n            stack.append(root.right)\n    while res_temp:\n        res.append(res_temp.pop())\n    return res\n\n\n# Recursive Implementation\ndef postorder_rec(root, res=None):\n    if root is None:\n        return []\n    if res is None:\n        res = []\n    postorder_rec(root.left, res)\n    postorder_rec(root.right, res)\n    res.append(root.val)\n    return res\n"
  },
  {
    "path": "algorithms/tree/traversal_preorder.py",
    "content": "\"\"\"\nTime complexity : O(n)\n\"\"\"\n\n\nclass Node:\n    \"\"\"This is a class of Node\"\"\"\n\n    def __init__(self, val, left=None, right=None):\n        self.val = val\n        self.left = left\n        self.right = right\n\n\ndef preorder(root):\n    \"\"\"Function to Preorder\"\"\"\n    res = []\n    if not root:\n        return res\n    stack = []\n    stack.append(root)\n    while stack:\n        root = stack.pop()\n        res.append(root.val)\n        if root.right:\n            stack.append(root.right)\n        if root.left:\n            stack.append(root.left)\n    return res\n\n\ndef preorder_rec(root, res=None):\n    \"\"\"Recursive Implementation\"\"\"\n    if root is None:\n        return []\n    if res is None:\n        res = []\n    res.append(root.val)\n    preorder_rec(root.left, res)\n    preorder_rec(root.right, res)\n    return res\n"
  },
  {
    "path": "algorithms/tree/traversal_zigzag.py",
    "content": "\"\"\"\nGiven a binary tree, return the zigzag level order traversal\nof its nodes' values.\n(ie, from left to right, then right to left\nfor the next level and alternate between).\n\nFor example:\nGiven binary tree [3,9,20,null,null,15,7],\n    3\n   / \\\n  9  20\n    /  \\\n   15   7\nreturn its zigzag level order traversal as:\n[\n  [3],\n  [20,9],\n  [15,7]\n]\n\"\"\"\n\n\ndef zigzag_level(root):\n    res = []\n    if not root:\n        return res\n    level = [root]\n    flag = 1\n    while level:\n        current = []\n        new_level = []\n        for node in level:\n            current.append(node.val)\n            if node.left:\n                new_level.append(node.left)\n            if node.right:\n                new_level.append(node.right)\n        level = new_level\n        res.append(current[::flag])\n        flag *= -1\n    return res\n"
  },
  {
    "path": "algorithms/tree/tree.py",
    "content": "\"\"\"Binary Tree Node (re-export).\n\nThis module re-exports TreeNode from the canonical location in\n``algorithms.common.tree_node`` for backward compatibility.\n\"\"\"\n\nfrom algorithms.common.tree_node import TreeNode\n\n__all__ = [\"TreeNode\"]\n"
  },
  {
    "path": "algorithms/tree/trie_add_and_search.py",
    "content": "\"\"\"\nWe are asked to design an efficient data structure\nthat allows us to add and search for words.\nThe search can be a literal word or regular expression\ncontaining “.”, where “.” can be any letter.\n\nExample:\naddWord(“bad”)\naddWord(“dad”)\naddWord(“mad”)\nsearch(“pad”) -> false\nsearch(“bad”) -> true\nsearch(“.ad”) -> true\nsearch(“b..”) -> true\n\"\"\"\n\nimport collections\n\n\nclass TrieNode:\n    def __init__(self, letter, is_terminal=False):\n        self.children = dict()\n        self.letter = letter\n        self.is_terminal = is_terminal\n\n\nclass WordDictionary:\n    def __init__(self):\n        self.root = TrieNode(\"\")\n\n    def add_word(self, word):\n        cur = self.root\n        for letter in word:\n            if letter not in cur.children:\n                cur.children[letter] = TrieNode(letter)\n            cur = cur.children[letter]\n        cur.is_terminal = True\n\n    def search(self, word, node=None):\n        cur = node\n        if not cur:\n            cur = self.root\n        for i, letter in enumerate(word):\n            # if dot\n            if letter == \".\":\n                if i == len(word) - 1:  # if last character\n                    return any(\n                        child.is_terminal\n                        for child in cur.children.itervalues()\n                    )\n                return any(\n                    self.search(word[i + 1 :], child)\n                    for child in cur.children.itervalues()\n                )\n            # if letter\n            if letter not in cur.children:\n                return False\n            cur = cur.children[letter]\n        return cur.is_terminal\n\n\nclass WordDictionary2:\n    def __init__(self):\n        self.word_dict = collections.defaultdict(list)\n\n    def add_word(self, word):\n        if word:\n            self.word_dict[len(word)].append(word)\n\n    def search(self, word):\n        if not word:\n            return False\n        if \".\" not in word:\n            return word in self.word_dict[len(word)]\n        for v in self.word_dict[len(word)]:\n            # match xx.xx.x with yyyyyyy\n            for i, ch in enumerate(word):\n                if ch != v[i] and ch != \".\":\n                    break\n            else:\n                return True\n        return False\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Algorithms - Pythonic Data Structures and Algorithms</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:#fff;color:#1a1a1a;line-height:1.4;padding:1rem;max-width:960px;margin:0 auto}\nheader{padding:1rem 0;border-bottom:2px solid #111}\nh1{font-size:1.4rem;font-weight:700}\nh1 span{font-weight:400;color:#666;font-size:.85rem;margin-left:.5rem}\n.meta{color:#666;font-size:.8rem;margin-top:.25rem}\n.meta a{color:#111}\n.search-box{position:sticky;top:0;background:#fff;padding:.75rem 0;z-index:10}\n.search-box input{width:100%;padding:.5rem .75rem;border:2px solid #111;border-radius:4px;font-size:.95rem;outline:none}\n.search-box input:focus{border-color:#0066ff}\n.stats{font-size:.75rem;color:#888;margin-top:.25rem}\n.category{margin-bottom:.5rem}\n.cat-header{font-size:.85rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#fff;background:#111;padding:.25rem .5rem;cursor:pointer;display:flex;justify-content:space-between;align-items:center;user-select:none;position:sticky;top:3.2rem;z-index:5}\n.cat-header:hover{background:#333}\n.cat-header .count{font-weight:400;font-size:.75rem;color:#aaa}\n.cat-header .arrow{transition:transform .15s}\n.cat-header.collapsed .arrow{transform:rotate(-90deg)}\n.algo-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:0;border-left:1px solid #ddd;border-right:1px solid #ddd}\n.algo-list.hidden{display:none}\n.algo{padding:.3rem .5rem;font-size:.8rem;border-bottom:1px solid #eee;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.algo:hover{background:#f5f5f5}\n.algo .desc{color:#888;font-size:.7rem;margin-left:.25rem}\nmark{background:#fff3b0;padding:0 1px;border-radius:2px}\n.no-results{text-align:center;padding:2rem;color:#888;font-size:.9rem;display:none}\n/* Modal */\n.modal-backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:100;justify-content:center;align-items:center}\n.modal-backdrop.open{display:flex}\n.modal{background:#fff;border-radius:6px;width:min(720px,95vw);max-height:90vh;display:flex;flex-direction:column;overflow:hidden}\n.modal-head{display:flex;justify-content:space-between;align-items:flex-start;padding:.75rem 1rem;border-bottom:1px solid #eee;gap:.5rem}\n.modal-head h2{font-size:1rem;font-weight:700;line-height:1.3}\n.modal-head h2 small{font-weight:400;color:#888;font-size:.75rem;display:block}\n.modal-close{background:none;border:none;font-size:1.4rem;cursor:pointer;color:#666;padding:0 .25rem;line-height:1;flex-shrink:0}\n.modal-close:hover{color:#111}\n.modal-body{overflow-y:auto;padding:1rem;font-size:.85rem}\n.modal-body .section-label{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#888;margin:1rem 0 .35rem;padding-bottom:.2rem;border-bottom:1px solid #eee}\n.modal-body .section-label:first-child{margin-top:0}\n.modal-body .mechanism{white-space:pre-wrap;font-size:.8rem;line-height:1.5;color:#333}\n.modal-body pre{background:#f6f6f6;border:1px solid #e0e0e0;border-radius:4px;padding:.75rem;overflow-x:auto;font-size:.75rem;line-height:1.5;tab-size:4}\n.modal-body code{font-family:\"SF Mono\",Menlo,Consolas,monospace}\n.modal-body .src-link{display:inline-block;margin-top:.5rem;font-size:.75rem;color:#0066ff;text-decoration:none}\n.modal-body .src-link:hover{text-decoration:underline}\n.modal-body .loading{color:#888;font-style:italic}\n@media(max-width:600px){\n .algo-list{grid-template-columns:1fr}\n .algo{font-size:.75rem;padding:.25rem .4rem}\n .cat-header{font-size:.8rem;top:3rem}\n .modal{width:100vw;height:100vh;max-height:100vh;border-radius:0}\n .modal-body pre{font-size:.65rem}\n}\n</style>\n</head>\n<body>\n<header>\n<h1>algorithms <span>v1.0.0</span></h1>\n<div class=\"meta\">Pythonic Data Structures and Algorithms &middot; <a href=\"https://github.com/keon/algorithms\">GitHub</a> &middot; <code>pip install algorithms</code></div>\n</header>\n<div class=\"search-box\">\n<input type=\"text\" id=\"search\" placeholder=\"Search 358 algorithms...\" autocomplete=\"off\" spellcheck=\"false\">\n<div class=\"stats\" id=\"stats\"></div>\n</div>\n<main id=\"main\"></main>\n<div class=\"no-results\" id=\"no-results\">No algorithms found.</div>\n\n<div class=\"modal-backdrop\" id=\"modal-backdrop\">\n<div class=\"modal\" id=\"modal\">\n<div class=\"modal-head\">\n<h2 id=\"modal-title\">Algorithm<small id=\"modal-cat\"></small></h2>\n<button class=\"modal-close\" id=\"modal-close\">&times;</button>\n</div>\n<div class=\"modal-body\" id=\"modal-body\"></div>\n</div>\n</div>\n\n<script>\nconst REPO=\"https://github.com/keon/algorithms/blob/main/algorithms\";\nconst RAW=\"https://raw.githubusercontent.com/keon/algorithms/main/algorithms\";\nconst DATA={\n\"Array\":[\"delete_nth\",\"flatten\",\"garage\",\"josephus\",\"limit\",\"longest_non_repeat\",\"max_ones_index\",\"merge_intervals\",\"missing_ranges\",\"move_zeros\",\"n_sum\",\"plus_one\",\"remove_duplicates\",\"rotate\",\"summarize_ranges\",\"three_sum\",\"top_1\",\"trimmean\",\"two_sum\"],\n\"Backtracking\":[\"add_operators\",\"anagram\",\"array_sum_combinations\",\"combination_sum\",\"factor_combinations\",\"find_words\",\"generate_abbreviations\",\"generate_parenthesis\",\"letter_combination\",\"minimax\",\"palindrome_partitioning\",\"pattern_match\",\"permute\",\"permute_unique\",\"subsets\",\"subsets_unique\"],\n\"Bit Manipulation\":[\"add_bitwise_operator\",\"binary_gap\",\"bit_operation\",\"bytes_int_conversion\",\"count_flips_to_convert\",\"count_ones\",\"find_difference\",\"find_missing_number\",\"flip_bit_longest_sequence\",\"gray_code\",\"has_alternative_bit\",\"insert_bit\",\"power_of_two\",\"remove_bit\",\"reverse_bits\",\"single_number\",\"single_number2\",\"single_number3\",\"subsets\",\"swap_pair\"],\n\"Compression\":[\"elias\",\"huffman_coding\",\"rle_compression\"],\n\"Data Structures\":[\"avl_tree\",\"b_tree\",\"bst\",\"fenwick_tree\",\"graph\",\"hash_table\",\"heap\",\"iterative_segment_tree\",\"kd_tree\",\"linked_list\",\"priority_queue\",\"queue\",\"red_black_tree\",\"segment_tree\",\"separate_chaining_hash_table\",\"sqrt_decomposition\",\"stack\",\"trie\",\"union_find\"],\n\"Dynamic Programming\":[\"bitmask\",\"buy_sell_stock\",\"climbing_stairs\",\"coin_change\",\"combination_sum\",\"count_paths_dp\",\"edit_distance\",\"egg_drop\",\"fib\",\"hosoya_triangle\",\"house_robber\",\"int_divide\",\"job_scheduling\",\"k_factor\",\"knapsack\",\"longest_common_subsequence\",\"longest_increasing\",\"matrix_chain_order\",\"max_product_subarray\",\"max_subarray\",\"min_cost_path\",\"num_decodings\",\"planting_trees\",\"regex_matching\",\"rod_cut\",\"word_break\"],\n\"Graph\":[\"a_star\",\"all_factors\",\"all_pairs_shortest_path\",\"bellman_ford\",\"blossom\",\"check_bipartite\",\"check_digraph_strongly_connected\",\"clone_graph\",\"count_connected_number_of_component\",\"count_islands_bfs\",\"count_islands_dfs\",\"count_islands_unionfind\",\"cycle_detection\",\"dijkstra\",\"dijkstra_heapq\",\"find_all_cliques\",\"find_path\",\"graph\",\"kahns_algorithm\",\"markov_chain\",\"maximum_flow\",\"maximum_flow_bfs\",\"maximum_flow_dfs\",\"maze_search_bfs\",\"maze_search_dfs\",\"minimum_spanning_tree\",\"pacific_atlantic\",\"path_between_two_vertices_in_digraph\",\"prims_minimum_spanning\",\"satisfiability\",\"shortest_distance_from_all_buildings\",\"strongly_connected_components_kosaraju\",\"sudoku_solver\",\"tarjan\",\"topological_sort_bfs\",\"topological_sort_dfs\",\"transitive_closure_dfs\",\"traversal\",\"walls_and_gates\",\"word_ladder\"],\n\"Greedy\":[\"gale_shapley\",\"max_contiguous_subsequence_sum\"],\n\"Heap\":[\"k_closest_points\",\"merge_sorted_k_lists\",\"skyline\",\"sliding_window_max\"],\n\"Linked List\":[\"add_two_numbers\",\"copy_random_pointer\",\"delete_node\",\"first_cyclic_node\",\"intersection\",\"is_cyclic\",\"is_palindrome\",\"is_sorted\",\"kth_to_last\",\"merge_two_list\",\"partition\",\"remove_duplicates\",\"remove_range\",\"reverse\",\"rotate_list\",\"swap_in_pairs\"],\n\"Map\":[\"is_anagram\",\"is_isomorphic\",\"longest_common_subsequence\",\"longest_palindromic_subsequence\",\"randomized_set\",\"valid_sudoku\",\"word_pattern\"],\n\"Math\":[\"base_conversion\",\"chinese_remainder_theorem\",\"combination\",\"cosine_similarity\",\"decimal_to_binary_ip\",\"diffie_hellman_key_exchange\",\"distance_between_two_points\",\"euler_totient\",\"extended_gcd\",\"factorial\",\"fft\",\"find_order_simple\",\"find_primitive_root_simple\",\"gcd\",\"generate_strobogrammtic\",\"goldbach\",\"hailstone\",\"is_strobogrammatic\",\"krishnamurthy_number\",\"linear_regression\",\"magic_number\",\"manhattan_distance\",\"modular_exponential\",\"modular_inverse\",\"next_bigger\",\"next_perfect_square\",\"nth_digit\",\"num_digits\",\"num_perfect_squares\",\"polynomial\",\"polynomial_division\",\"power\",\"prime_check\",\"primes_sieve_of_eratosthenes\",\"pythagoras\",\"rabin_miller\",\"recursive_binomial_coefficient\",\"rsa\",\"sqrt_precision_factor\",\"summing_digits\",\"surface_area_of_torus\",\"symmetry_group_cycle_index\"],\n\"Matrix\":[\"bomb_enemy\",\"cholesky_matrix_decomposition\",\"copy_transform\",\"count_paths\",\"crout_matrix_decomposition\",\"matrix_exponentiation\",\"matrix_inversion\",\"multiply\",\"rotate_image\",\"search_in_sorted_matrix\",\"sort_matrix_diagonally\",\"sparse_dot_vector\",\"sparse_mul\",\"spiral_traversal\",\"sudoku_validator\",\"sum_sub_squares\"],\n\"Queue\":[\"max_sliding_window\",\"moving_average\",\"reconstruct_queue\",\"zigzagiterator\"],\n\"Searching\":[\"binary_search\",\"exponential_search\",\"find_min_rotate\",\"first_occurrence\",\"generalized_binary_search\",\"interpolation_search\",\"jump_search\",\"last_occurrence\",\"linear_search\",\"next_greatest_letter\",\"search_insert\",\"search_range\",\"search_rotate\",\"sentinel_search\",\"ternary_search\",\"two_sum\"],\n\"Set\":[\"find_keyboard_row\",\"randomized_set\",\"set_covering\"],\n\"Sorting\":[\"bead_sort\",\"bitonic_sort\",\"bogo_sort\",\"bubble_sort\",\"bucket_sort\",\"cocktail_shaker_sort\",\"comb_sort\",\"counting_sort\",\"cycle_sort\",\"exchange_sort\",\"gnome_sort\",\"heap_sort\",\"insertion_sort\",\"meeting_rooms\",\"merge_sort\",\"pancake_sort\",\"pigeonhole_sort\",\"quick_sort\",\"radix_sort\",\"selection_sort\",\"shell_sort\",\"sort_colors\",\"stooge_sort\",\"wiggle_sort\"],\n\"Stack\":[\"is_consecutive\",\"is_sorted\",\"longest_abs_path\",\"ordered_stack\",\"remove_min\",\"simplify_path\",\"stutter\",\"switch_pairs\",\"valid_parenthesis\"],\n\"Streaming\":[\"misra_gries\",\"one_sparse_recovery\"],\n\"String\":[\"add_binary\",\"alphabet_board_path\",\"atbash_cipher\",\"breaking_bad\",\"caesar_cipher\",\"check_pangram\",\"contain_string\",\"count_binary_substring\",\"decode_string\",\"delete_reoccurring\",\"domain_extractor\",\"encode_decode\",\"first_unique_char\",\"fizzbuzz\",\"group_anagrams\",\"int_to_roman\",\"is_palindrome\",\"is_rotated\",\"judge_circle\",\"knuth_morris_pratt\",\"license_number\",\"longest_common_prefix\",\"longest_palindromic_substring\",\"make_sentence\",\"manacher\",\"merge_string_checker\",\"min_distance\",\"multiply_strings\",\"one_edit_distance\",\"panagram\",\"rabin_karp\",\"repeat_string\",\"repeat_substring\",\"reverse_string\",\"reverse_vowel\",\"reverse_words\",\"roman_to_int\",\"rotate\",\"strip_url_params\",\"strong_password\",\"swap_characters\",\"text_justification\",\"unique_morse\",\"validate_coordinates\",\"word_squares\",\"z_algorithm\"],\n\"Tree\":[\"bin_tree_to_list\",\"binary_tree_paths\",\"binary_tree_views\",\"bst_array_to_bst\",\"bst_closest_value\",\"bst_count_left_node\",\"bst_delete_node\",\"bst_depth_sum\",\"bst_height\",\"bst_is_bst\",\"bst_iterator\",\"bst_kth_smallest\",\"bst_lowest_common_ancestor\",\"bst_num_empty\",\"bst_predecessor\",\"bst_serialize_deserialize\",\"bst_successor\",\"bst_unique_bst\",\"bst_validate_bst\",\"construct_tree_postorder_preorder\",\"deepest_left\",\"invert_tree\",\"is_balanced\",\"is_subtree\",\"is_symmetric\",\"longest_consecutive\",\"lowest_common_ancestor\",\"max_height\",\"max_path_sum\",\"min_height\",\"path_sum\",\"path_sum2\",\"pretty_print\",\"same_tree\",\"traversal_inorder\",\"traversal_level_order\",\"traversal_postorder\",\"traversal_preorder\",\"traversal_zigzag\",\"tree\",\"trie_add_and_search\"]\n};\n\nconst DESC={\"array/delete_nth\":\"Keep at most N occurrences of each element in an array\",\"array/max_ones_index\":\"Find the 0 to flip for the longest run of 1s in a binary array\",\"array/plus_one\":\"Add one to a number represented as a digit array\",\"array/remove_duplicates\":\"Remove duplicate elements from an array preserving order\",\"array/summarize_ranges\":\"Summarize consecutive runs in a sorted array as ranges\",\"array/three_sum\":\"Find all unique triplets in an array that sum to zero\",\"array/top_1\":\"Find the most frequently occurring value(s) in an array\",\"array/two_sum\":\"Find two indices whose values sum to a target\",\"array/flatten\":\"Flatten a nested iterable into a single list\",\"array/garage\":\"Find minimum swaps to rearrange a parking lot with one empty spot\",\"array/josephus\":\"Yield elimination order in the Josephus circle problem\",\"array/limit\":\"Filter array elements to those within a min/max range\",\"array/longest_non_repeat\":\"Find the longest substring without repeating characters\",\"array/merge_intervals\":\"Merge overlapping intervals into a consolidated set\",\"array/missing_ranges\":\"Find gaps in a sorted array between low and high bounds\",\"array/move_zeros\":\"Move all zeros to the end while preserving element order\",\"array/n_sum\":\"Find all unique n-tuples in an array that sum to a target\",\"array/rotate\":\"Rotate an array to the right by k positions\",\"array/trimmean\":\"Compute the mean after discarding extreme values by percentage\",\"backtracking/anagram\":\"Check whether two strings are anagrams of each other\",\"backtracking/array_sum_combinations\":\"Find three-array element combos that sum to a target\",\"backtracking/combination_sum\":\"Find all unique combinations of candidates summing to target\",\"backtracking/generate_parenthesis\":\"Generate all valid combinations of n pairs of parentheses\",\"backtracking/letter_combination\":\"Map digit string to all possible letter combinations\",\"backtracking/permute_unique\":\"Return all unique permutations of a list with duplicates\",\"backtracking/subsets\":\"Generate all possible subsets (power set) of distinct integers\",\"backtracking/subsets_unique\":\"Generate all unique subsets of a list that may have duplicates\",\"backtracking/add_operators\":\"Insert +, -, * between digits to reach a target value\",\"backtracking/factor_combinations\":\"Find all ways to factor a number into smaller factors\",\"backtracking/find_words\":\"Find words from a list that can be traced on a character board\",\"backtracking/generate_abbreviations\":\"Generate all generalized abbreviations of a word\",\"backtracking/palindrome_partitioning\":\"Partition a string into all possible palindromic substrings\",\"backtracking/pattern_match\":\"Check if a string matches a pattern via bijective mapping\",\"backtracking/permute\":\"Generate all permutations of a collection of distinct elements\",\"backtracking/minimax\":\"Find optimal move in a two-player game with alpha-beta pruning\",\"bit_manipulation/add_bitwise_operator\":\"Add two integers using only bitwise operations\",\"bit_manipulation/binary_gap\":\"Find the longest gap between consecutive 1-bits in binary\",\"bit_manipulation/bit_operation\":\"Get, set, clear, and update individual bits in an integer\",\"bit_manipulation/bytes_int_conversion\":\"Convert between integers and byte sequences\",\"bit_manipulation/find_difference\":\"Find the extra character added to a shuffled string\",\"bit_manipulation/find_missing_number\":\"Find the missing number in a sequence from 0 to n\",\"bit_manipulation/insert_bit\":\"Insert one or more bits into an integer at a given position\",\"bit_manipulation/power_of_two\":\"Check whether an integer is a power of two\",\"bit_manipulation/remove_bit\":\"Remove a bit at a specific position from an integer\",\"bit_manipulation/reverse_bits\":\"Reverse all 32 bits of an unsigned integer\",\"bit_manipulation/single_number\":\"Find the element that appears once; others appear twice\",\"bit_manipulation/single_number2\":\"Find the element appearing once; others appear three times\",\"bit_manipulation/single_number3\":\"Find two unique elements; all others appear exactly twice\",\"bit_manipulation/subsets\":\"Generate all subsets of a set using bitmask enumeration\",\"bit_manipulation/count_flips_to_convert\":\"Count bit flips needed to convert one integer to another\",\"bit_manipulation/count_ones\":\"Count the number of set bits (Hamming weight) in an integer\",\"bit_manipulation/flip_bit_longest_sequence\":\"Find longest 1-bit run achievable by flipping one 0-bit\",\"bit_manipulation/has_alternative_bit\":\"Check whether an integer has alternating bit pattern\",\"bit_manipulation/swap_pair\":\"Swap every pair of adjacent bits in an integer\",\"bit_manipulation/gray_code\":\"Generate n-bit Gray code sequences and convert back to binary\",\"compression/rle_compression\":\"Encode and decode strings using run-length encoding\",\"compression/elias\":\"Encode positive integers using Elias gamma and delta coding\",\"compression/huffman_coding\":\"Compress and decompress files using Huffman coding\",\"data_structures/separate_chaining_hash_table\":\"Hash table using separate chaining for collision resolution\",\"data_structures/linked_list\":\"Define singly and doubly linked list node classes\",\"data_structures/graph\":\"Represent directed graphs with nodes, edges, and adjacency lists\",\"data_structures/union_find\":\"Disjoint set with union by size and path compression\",\"data_structures/avl_tree\":\"Self-balancing AVL tree with insert and in-order traversal\",\"data_structures/b_tree\":\"Self-balancing B-tree with search, insert, and delete\",\"data_structures/bst\":\"Binary search tree with insert, search, and traversals\",\"data_structures/fenwick_tree\":\"Binary indexed tree for prefix sums and point updates\",\"data_structures/hash_table\":\"Hash table using open addressing with linear probing\",\"data_structures/heap\":\"Min binary heap with insert and remove-min operations\",\"data_structures/iterative_segment_tree\":\"Iterative segment tree for range queries and point updates\",\"data_structures/priority_queue\":\"Priority queue backed by a sorted linear array\",\"data_structures/queue\":\"Queue ADT implemented with array and linked list\",\"data_structures/red_black_tree\":\"Red-black tree with insert, delete, and rebalancing\",\"data_structures/segment_tree\":\"Recursive segment tree for range queries\",\"data_structures/stack\":\"Stack ADT implemented with array and linked list\",\"data_structures/trie\":\"Trie (prefix tree) with insert, search, and starts-with\",\"data_structures/kd_tree\":\"KD-tree for k-dimensional nearest-neighbour queries\",\"data_structures/sqrt_decomposition\":\"Sqrt decomposition for range sum queries and point updates\",\"dynamic_programming/buy_sell_stock\":\"Find maximum profit from a single buy-sell stock transaction\",\"dynamic_programming/climbing_stairs\":\"Count distinct ways to climb n steps using 1 or 2 steps\",\"dynamic_programming/coin_change\":\"Count coin combinations that sum to a given value\",\"dynamic_programming/edit_distance\":\"Find minimum edits to transform one string into another\",\"dynamic_programming/egg_drop\":\"Find minimum trials to identify the critical floor\",\"dynamic_programming/hosoya_triangle\":\"Compute entries of the Hosoya (Fibonacci) triangle\",\"dynamic_programming/house_robber\":\"Find max robbery amount without hitting adjacent houses\",\"dynamic_programming/int_divide\":\"Count partitions of a positive integer into sums\",\"dynamic_programming/job_scheduling\":\"Find max profit from non-overlapping weighted jobs\",\"dynamic_programming/knapsack\":\"Find max value fitting in a 0/1 knapsack with capacity limit\",\"dynamic_programming/longest_common_subsequence\":\"Find length of longest common subsequence of two strings\",\"dynamic_programming/longest_increasing\":\"Find length of the longest increasing subsequence\",\"dynamic_programming/max_subarray\":\"Find contiguous subarray with the largest sum\",\"dynamic_programming/rod_cut\":\"Find max revenue from cutting a rod into priced pieces\",\"dynamic_programming/word_break\":\"Check if a string can be segmented into dictionary words\",\"dynamic_programming/combination_sum\":\"Count ordered combinations of numbers summing to target\",\"dynamic_programming/fib\":\"Compute the n-th Fibonacci number\",\"dynamic_programming/k_factor\":\"Count strings of given length with exactly k 'abba' substrings\",\"dynamic_programming/matrix_chain_order\":\"Find optimal parenthesization to minimize matrix multiplications\",\"dynamic_programming/max_product_subarray\":\"Find contiguous subarray with the largest product\",\"dynamic_programming/min_cost_path\":\"Find minimum cost to travel from first to last station\",\"dynamic_programming/num_decodings\":\"Count ways to decode a digit string into letters A-Z\",\"dynamic_programming/planting_trees\":\"Find min distance to rearrange trees into valid positions\",\"dynamic_programming/regex_matching\":\"Match strings against patterns with '.' and '*' support\",\"dynamic_programming/bitmask\":\"Solve the Travelling Salesman Problem using bitmask DP\",\"dynamic_programming/count_paths_dp\":\"Count unique paths in an m x n grid moving right or down\",\"graph/bellman_ford\":\"Find shortest paths from source, handling negative edge weights\",\"graph/check_bipartite\":\"Check whether an undirected graph is bipartite via BFS\",\"graph/check_digraph_strongly_connected\":\"Check if a directed graph is strongly connected\",\"graph/clone_graph\":\"Deep-clone an undirected graph using BFS or DFS\",\"graph/count_connected_number_of_component\":\"Count connected components in an undirected graph\",\"graph/cycle_detection\":\"Detect cycles in a directed graph using DFS colouring\",\"graph/dijkstra\":\"Find shortest paths from source with non-negative weights\",\"graph/find_all_cliques\":\"Find all maximal cliques using Bron-Kerbosch\",\"graph/markov_chain\":\"Simulate state transitions in a discrete Markov chain\",\"graph/maximum_flow_bfs\":\"Compute max flow using BFS augmenting paths\",\"graph/maximum_flow_dfs\":\"Compute max flow using DFS augmenting paths\",\"graph/minimum_spanning_tree\":\"Find MST weight using Kruskal's union-find approach\",\"graph/path_between_two_vertices_in_digraph\":\"Check if a path exists between two vertices in a digraph\",\"graph/prims_minimum_spanning\":\"Find MST weight using Prim's priority-queue approach\",\"graph/satisfiability\":\"Solve 2-SAT and find a satisfying variable assignment\",\"graph/tarjan\":\"Find all strongly connected components via Tarjan's method\",\"graph/transitive_closure_dfs\":\"Compute the transitive closure matrix of a directed graph\",\"graph/traversal\":\"Traverse a graph using DFS and BFS\",\"graph/topological_sort_bfs\":\"Produce topological ordering of a DAG using BFS (Kahn's)\",\"graph/all_factors\":\"Find all factor combinations of an integer\",\"graph/count_islands_dfs\":\"Count islands in a 2D grid using DFS flood-fill\",\"graph/pacific_atlantic\":\"Find cells where water flows to both Pacific and Atlantic\",\"graph/walls_and_gates\":\"Fill rooms with distance to nearest gate via DFS\",\"graph/count_islands_unionfind\":\"Count islands incrementally using union-find\",\"graph/graph\":\"Re-export Node, DirectedEdge, and DirectedGraph data structures\",\"graph/a_star\":\"Find shortest path using A* with a heuristic function\",\"graph/all_pairs_shortest_path\":\"Compute all-pairs shortest paths via Floyd-Warshall\",\"graph/count_islands_bfs\":\"Count islands in a 2D grid using BFS\",\"graph/find_path\":\"Find single, all, or shortest paths between two nodes\",\"graph/kahns_algorithm\":\"Topological sort via Kahn's in-degree BFS approach\",\"graph/maximum_flow\":\"Compute max flow via Ford-Fulkerson, Edmonds-Karp, or Dinic\",\"graph/maze_search_bfs\":\"Find shortest path through a maze using BFS\",\"graph/maze_search_dfs\":\"Find shortest path through a maze using DFS backtracking\",\"graph/shortest_distance_from_all_buildings\":\"Find land cell with min total distance to all buildings\",\"graph/strongly_connected_components_kosaraju\":\"Count strongly connected components via Kosaraju's method\",\"graph/sudoku_solver\":\"Solve a Sudoku puzzle using constraint propagation and DFS\",\"graph/topological_sort_dfs\":\"Produce topological ordering of a DAG using DFS\",\"graph/word_ladder\":\"Find shortest word transformation sequence via bidirectional BFS\",\"graph/blossom\":\"Find maximum cardinality matching in a general graph\",\"graph/dijkstra_heapq\":\"Find shortest path using heap-optimised Dijkstra's\",\"greedy/gale_shapley\":\"Find a stable matching between two groups using ranked preferences\",\"greedy/max_contiguous_subsequence_sum\":\"Find the maximum sum of a contiguous subarray\",\"heap/skyline\":\"Compute the skyline contour from a list of building triplets\",\"heap/sliding_window_max\":\"Find the maximum element in each sliding window of size k\",\"heap/k_closest_points\":\"Find the k closest points to the origin using a max heap\",\"heap/merge_sorted_k_lists\":\"Merge k sorted linked lists into one sorted linked list\",\"linked_list/add_two_numbers\":\"Add two numbers represented as reversed linked lists\",\"linked_list/copy_random_pointer\":\"Deep-copy a linked list with random pointers\",\"linked_list/delete_node\":\"Delete a node from a singly linked list given only that node\",\"linked_list/first_cyclic_node\":\"Find the first node of a cycle using Floyd's method\",\"linked_list/intersection\":\"Find the intersection node of two singly linked lists\",\"linked_list/is_cyclic\":\"Detect whether a linked list contains a cycle\",\"linked_list/is_sorted\":\"Check if a linked list is sorted in non-decreasing order\",\"linked_list/merge_two_list\":\"Merge two sorted linked lists into one sorted list\",\"linked_list/partition\":\"Partition a linked list around a given value\",\"linked_list/remove_duplicates\":\"Remove duplicate values from an unsorted linked list\",\"linked_list/remove_range\":\"Remove nodes in an index range from a linked list\",\"linked_list/reverse\":\"Reverse a singly linked list iteratively or recursively\",\"linked_list/rotate_list\":\"Rotate a linked list to the right by k positions\",\"linked_list/swap_in_pairs\":\"Swap every two adjacent nodes in a linked list\",\"linked_list/is_palindrome\":\"Check if a singly linked list is a palindrome\",\"linked_list/kth_to_last\":\"Find the kth to last element of a singly linked list\",\"map/is_anagram\":\"Check if two strings are anagrams of each other\",\"map/is_isomorphic\":\"Check if two strings are isomorphic via character mapping\",\"map/longest_common_subsequence\":\"Find the longest common substring between two strings\",\"map/longest_palindromic_subsequence\":\"Find the length of the longest palindromic substring\",\"map/randomized_set\":\"Set supporting O(1) insert, remove, and random element access\",\"map/valid_sudoku\":\"Check whether a 9x9 Sudoku board configuration is valid\",\"map/word_pattern\":\"Check if a string follows a given letter-to-word pattern\",\"math/combination\":\"Compute n-choose-r using recursive and memoized approaches\",\"math/extended_gcd\":\"Find Bezout coefficients using the extended Euclidean method\",\"math/factorial\":\"Compute the factorial of a non-negative integer\",\"math/fft\":\"Compute the discrete Fourier transform via Cooley-Tukey FFT\",\"math/find_order_simple\":\"Find the multiplicative order of a modulo n\",\"math/gcd\":\"Compute greatest common divisor and least common multiple\",\"math/generate_strobogrammtic\":\"Generate all strobogrammatic numbers of a given length\",\"math/krishnamurthy_number\":\"Check if a number equals the sum of factorials of its digits\",\"math/magic_number\":\"Check if recursive digit summing yields 1 (digital root)\",\"math/modular_exponential\":\"Compute (base ^ exponent) % mod via binary exponentiation\",\"math/modular_inverse\":\"Find modular multiplicative inverse using extended GCD\",\"math/nth_digit\":\"Find the nth digit in the sequence of natural numbers\",\"math/num_digits\":\"Count the number of digits in an integer\",\"math/power\":\"Compute a^n efficiently using exponentiation by squaring\",\"math/prime_check\":\"Check if an integer is prime using trial division\",\"math/primes_sieve_of_eratosthenes\":\"Generate all primes up to n using the Sieve of Eratosthenes\",\"math/sqrt_precision_factor\":\"Compute square root using Newton's method with precision\",\"math/base_conversion\":\"Convert integers between arbitrary bases (2-36)\",\"math/chinese_remainder_theorem\":\"Solve a system of simultaneous congruences\",\"math/cosine_similarity\":\"Compute cosine similarity between two vectors\",\"math/decimal_to_binary_ip\":\"Convert a dotted-decimal IP address to binary\",\"math/diffie_hellman_key_exchange\":\"Perform Diffie-Hellman key exchange and verify shared keys\",\"math/distance_between_two_points\":\"Compute Euclidean distance between two 2D points\",\"math/euler_totient\":\"Compute Euler's totient function phi(n)\",\"math/find_primitive_root_simple\":\"Find all primitive roots of a positive integer\",\"math/hailstone\":\"Generate the hailstone (Collatz) sequence from n to 1\",\"math/is_strobogrammatic\":\"Check if a number looks the same rotated 180 degrees\",\"math/next_bigger\":\"Find the next higher number using the same digits\",\"math/next_perfect_square\":\"Find the next perfect square if input is a perfect square\",\"math/num_perfect_squares\":\"Find minimum count of perfect squares that sum to n\",\"math/polynomial\":\"Symbolic polynomial and monomial arithmetic with exact fractions\",\"math/pythagoras\":\"Compute the unknown side of a right triangle\",\"math/rabin_miller\":\"Test probable primality using the Rabin-Miller method\",\"math/recursive_binomial_coefficient\":\"Compute binomial coefficient C(n,k) recursively\",\"math/rsa\":\"Generate RSA keys and perform encryption/decryption\",\"math/summing_digits\":\"Find numbers where positional-power digit sums equal themselves\",\"math/surface_area_of_torus\":\"Compute the surface area of a torus from its radii\",\"math/symmetry_group_cycle_index\":\"Compute the cycle index of symmetric group S_n\",\"math/linear_regression\":\"Fit a least-squares regression line to (x, y) data\",\"math/manhattan_distance\":\"Compute Manhattan (L1) distance between two points\",\"math/polynomial_division\":\"Divide one polynomial by another via long division\",\"math/goldbach\":\"Find two primes that sum to a given even integer\",\"matrix/bomb_enemy\":\"Find max enemies killed by placing one bomb in a grid\",\"matrix/cholesky_matrix_decomposition\":\"Decompose a positive-definite matrix into lower-triangular form\",\"matrix/copy_transform\":\"Rotate and transpose a matrix by creating transformed copies\",\"matrix/count_paths\":\"Count unique paths from top-left to bottom-right of a grid\",\"matrix/crout_matrix_decomposition\":\"Decompose a matrix into lower and upper triangular matrices\",\"matrix/matrix_exponentiation\":\"Compute the n-th power of a square matrix via repeated squaring\",\"matrix/matrix_inversion\":\"Compute the inverse of an n x n matrix using cofactor expansion\",\"matrix/multiply\":\"Multiply two compatible matrices and return their product\",\"matrix/rotate_image\":\"Rotate a square matrix 90 degrees clockwise in-place\",\"matrix/search_in_sorted_matrix\":\"Search for a value in a row-wise and column-wise sorted matrix\",\"matrix/sort_matrix_diagonally\":\"Sort each top-left to bottom-right diagonal in ascending order\",\"matrix/sparse_dot_vector\":\"Compute dot product of two sparse vectors efficiently\",\"matrix/sparse_mul\":\"Multiply two sparse matrices, skipping zero elements\",\"matrix/spiral_traversal\":\"Traverse matrix elements in spiral order\",\"matrix/sudoku_validator\":\"Validate whether a completed 9x9 Sudoku board is correct\",\"matrix/sum_sub_squares\":\"Compute sums of all k x k sub-squares in a matrix\",\"queue/max_sliding_window\":\"Find the maximum element in each sliding window of size k\",\"queue/moving_average\":\"Compute moving average of a data stream over a fixed window\",\"queue/reconstruct_queue\":\"Reconstruct a queue from (height, k) pairs by sorting\",\"queue/zigzagiterator\":\"Interleave elements from two lists in zigzag fashion\",\"searching/binary_search\":\"Search for a value in a sorted array by halving the interval\",\"searching/exponential_search\":\"Search by doubling index to find range, then binary search\",\"searching/find_min_rotate\":\"Find the minimum element in a rotated sorted array\",\"searching/first_occurrence\":\"Find the index of the first occurrence in a sorted array\",\"searching/generalized_binary_search\":\"Find smallest value where a monotonic predicate is true\",\"searching/interpolation_search\":\"Search a uniformly distributed sorted array via interpolation\",\"searching/jump_search\":\"Search a sorted array by jumping in fixed-size blocks\",\"searching/last_occurrence\":\"Find the index of the last occurrence in a sorted array\",\"searching/linear_search\":\"Search for a value by checking every element sequentially\",\"searching/next_greatest_letter\":\"Find the smallest letter greater than a given target\",\"searching/search_insert\":\"Find index of target or its sorted insertion position\",\"searching/search_range\":\"Find first and last positions of a target in a sorted array\",\"searching/search_rotate\":\"Search for a value in a rotated sorted array\",\"searching/sentinel_search\":\"Linear search optimized with a sentinel to skip bounds checks\",\"searching/ternary_search\":\"Search a sorted array by dividing the range into three parts\",\"searching/two_sum\":\"Find two numbers in an array that add up to a target sum\",\"set/find_keyboard_row\":\"Find words that can be typed using one keyboard row\",\"set/randomized_set\":\"Insert, remove, and get random element in O(1) average time\",\"set/set_covering\":\"Find minimum-cost sub-collection of sets that covers a universe\",\"sorting/bead_sort\":\"Sort non-negative integers by simulating beads settling on abacus\",\"sorting/bitonic_sort\":\"Sort by recursively building and merging bitonic sequences\",\"sorting/bogo_sort\":\"Sort by randomly shuffling until the array is ordered\",\"sorting/bubble_sort\":\"Sort by repeatedly swapping adjacent out-of-order elements\",\"sorting/bucket_sort\":\"Sort by distributing elements into buckets, then sorting each\",\"sorting/cocktail_shaker_sort\":\"Sort by bubbling in both directions alternately\",\"sorting/comb_sort\":\"Sort by comparing elements with a shrinking gap sequence\",\"sorting/counting_sort\":\"Sort integers by counting occurrences of each value\",\"sorting/cycle_sort\":\"Sort by rotating cycles to minimize write operations\",\"sorting/exchange_sort\":\"Sort by comparing and swapping every pair of elements\",\"sorting/gnome_sort\":\"Sort by swapping backward until the element is in place\",\"sorting/heap_sort\":\"Sort by building a heap and repeatedly extracting the extreme\",\"sorting/insertion_sort\":\"Sort by inserting each element into its correct position\",\"sorting/meeting_rooms\":\"Check whether all meeting intervals can be attended without overlap\",\"sorting/merge_sort\":\"Sort by dividing in half, sorting each, and merging back\",\"sorting/pancake_sort\":\"Sort by flipping subarrays to move max elements into place\",\"sorting/pigeonhole_sort\":\"Sort integers when range is close to the number of elements\",\"sorting/quick_sort\":\"Sort by partitioning around a pivot and recursing on halves\",\"sorting/radix_sort\":\"Sort integers by processing digits from least to most significant\",\"sorting/selection_sort\":\"Sort by repeatedly selecting the minimum from unsorted portion\",\"sorting/shell_sort\":\"Sort using insertion sort with a shrinking gap between elements\",\"sorting/sort_colors\":\"Sort array of 0s, 1s, and 2s in-place (Dutch National Flag)\",\"sorting/stooge_sort\":\"Sort by recursively sorting overlapping two-thirds of the array\",\"sorting/wiggle_sort\":\"Reorder array so that nums[0]<nums[1]>nums[2]<nums[3]...\",\"stack/is_consecutive\":\"Check if a stack contains consecutive integers from bottom to top\",\"stack/is_sorted\":\"Check if a stack is sorted in ascending order from bottom to top\",\"stack/longest_abs_path\":\"Find the length of the longest absolute file path in a string\",\"stack/ordered_stack\":\"Implement a stack that maintains elements in sorted order\",\"stack/remove_min\":\"Remove the minimum value from a stack preserving element order\",\"stack/simplify_path\":\"Simplify a Unix-style absolute file path\",\"stack/stutter\":\"Replace every value in a stack with two copies of that value\",\"stack/switch_pairs\":\"Swap successive pairs of values in a stack from the bottom\",\"stack/valid_parenthesis\":\"Check if a string of brackets is properly closed and nested\",\"streaming/misra_gries\":\"Find all elements appearing at least n/k times in a stream\",\"streaming/one_sparse_recovery\":\"Recover the unique element from a 1-sparse data stream\",\"string/add_binary\":\"Add two binary number strings and return their binary sum\",\"string/atbash_cipher\":\"Encrypt or decrypt a string using the Atbash cipher\",\"string/breaking_bad\":\"Match and bracket chemical symbols within words using a trie\",\"string/caesar_cipher\":\"Encrypt a string by shifting letters a fixed number of positions\",\"string/check_pangram\":\"Check if a string contains every letter of the alphabet\",\"string/contain_string\":\"Find the first occurrence of a substring in a string\",\"string/count_binary_substring\":\"Count substrings with equal grouped consecutive 0s and 1s\",\"string/decode_string\":\"Decode a string with nested k[encoded_string] repeat patterns\",\"string/delete_reoccurring\":\"Remove duplicate characters keeping only the first occurrence\",\"string/domain_extractor\":\"Extract the domain name from a URL string\",\"string/encode_decode\":\"Encode a list of strings into one string and decode it back\",\"string/first_unique_char\":\"Find the index of the first non-repeating character in a string\",\"string/fizzbuzz\":\"Generate FizzBuzz sequence replacing multiples of 3 and 5\",\"string/group_anagrams\":\"Group a list of strings by anagram equivalence\",\"string/is_palindrome\":\"Check if a string reads the same forwards and backwards\",\"string/is_rotated\":\"Check if one string is a rotation of another\",\"string/judge_circle\":\"Check if a sequence of moves returns a robot to the origin\",\"string/knuth_morris_pratt\":\"Find all pattern occurrences in text using KMP search\",\"string/longest_common_prefix\":\"Find the longest common prefix among an array of strings\",\"string/longest_palindromic_substring\":\"Find the longest palindromic substring in a string\",\"string/make_sentence\":\"Count ways to segment a string into dictionary words\",\"string/merge_string_checker\":\"Check if a string is a valid interleaving of two others\",\"string/min_distance\":\"Find minimum deletions to make two strings equal\",\"string/multiply_strings\":\"Multiply two numbers represented as strings\",\"string/one_edit_distance\":\"Check if two strings are exactly one edit apart\",\"string/rabin_karp\":\"Find pattern in text using Rabin-Karp rolling hash search\",\"string/reverse_string\":\"Reverse a string using recursive, iterative, and slice methods\",\"string/roman_to_int\":\"Convert a Roman numeral string to an integer\",\"string/rotate\":\"Rotate a string left by k positions\",\"string/strip_url_params\":\"Remove duplicate and specified query parameters from a URL\",\"string/strong_password\":\"Calculate minimum characters needed for a strong password\",\"string/text_justification\":\"Justify text to a fixed width with evenly distributed spaces\",\"string/unique_morse\":\"Count unique Morse code representations for a list of words\",\"string/validate_coordinates\":\"Validate if a string contains valid geographic coordinates\",\"string/int_to_roman\":\"Convert an integer to its Roman numeral representation\",\"string/license_number\":\"Reformat a license key string into groups of a given size\",\"string/panagram\":\"Check if a sentence uses every letter of the alphabet\",\"string/repeat_string\":\"Find minimum string repetitions to contain another as substring\",\"string/repeat_substring\":\"Check if a string is built from a repeated substring pattern\",\"string/reverse_vowel\":\"Reverse only the vowels in a string\",\"string/reverse_words\":\"Reverse the order of words in a string\",\"string/word_squares\":\"Find all valid word squares from a list of same-length words\",\"string/alphabet_board_path\":\"Find move sequence to spell a word on a 5x5 letter board\",\"string/manacher\":\"Find the longest palindromic substring in linear time\",\"string/swap_characters\":\"Check if one character swap can make two strings equal\",\"string/z_algorithm\":\"Find all pattern occurrences in text using the Z-array\",\"tree/binary_tree_paths\":\"Return all root-to-leaf paths in a binary tree\",\"tree/bst_array_to_bst\":\"Convert a sorted array to a height-balanced BST\",\"tree/bst_closest_value\":\"Find the BST value closest to a given target\",\"tree/bst_count_left_node\":\"Count the number of left children in a binary tree\",\"tree/bst_delete_node\":\"Delete a node with a given key from a BST\",\"tree/bst_depth_sum\":\"Compute depth-weighted sum of all node values in a BST\",\"tree/bst_height\":\"Compute the height (number of levels) of a binary tree\",\"tree/bst_is_bst\":\"Check if a binary tree is a valid BST using inorder traversal\",\"tree/bst_iterator\":\"Implement an iterator over BST nodes in inorder sequence\",\"tree/bst_kth_smallest\":\"Find the kth smallest element in a BST\",\"tree/bst_lowest_common_ancestor\":\"Find the lowest common ancestor of two nodes in a BST\",\"tree/bst_num_empty\":\"Count the number of empty branches in a binary tree\",\"tree/bst_serialize_deserialize\":\"Serialize and deserialize a binary tree to/from a string\",\"tree/bst_unique_bst\":\"Count structurally unique BSTs that store values 1 to n\",\"tree/bst_validate_bst\":\"Validate if a binary tree satisfies BST properties recursively\",\"tree/construct_tree_postorder_preorder\":\"Build a binary tree from preorder and postorder traversals\",\"tree/deepest_left\":\"Find the deepest left-child node in a binary tree\",\"tree/path_sum\":\"Check if a root-to-leaf path with a given sum exists\",\"tree/path_sum2\":\"Find all root-to-leaf paths that sum to a target value\",\"tree/same_tree\":\"Check if two binary trees are structurally identical\",\"tree/traversal_inorder\":\"Traverse a binary tree in inorder (left, root, right)\",\"tree/traversal_postorder\":\"Traverse a binary tree in postorder (left, right, root)\",\"tree/traversal_preorder\":\"Traverse a binary tree in preorder (root, left, right)\",\"tree/trie_add_and_search\":\"Add and search words in a trie with wildcard support\",\"tree/bin_tree_to_list\":\"Convert a binary tree to a sorted doubly linked list\",\"tree/invert_tree\":\"Invert a binary tree by swapping left and right children\",\"tree/is_balanced\":\"Check if a binary tree is height-balanced\",\"tree/is_subtree\":\"Check if one binary tree is a subtree of another\",\"tree/is_symmetric\":\"Check if a binary tree is a mirror of itself\",\"tree/longest_consecutive\":\"Find the longest parent-to-child consecutive value sequence\",\"tree/lowest_common_ancestor\":\"Find the lowest common ancestor of two nodes in a tree\",\"tree/max_height\":\"Find the maximum depth of a binary tree\",\"tree/max_path_sum\":\"Find the maximum path sum in a binary tree\",\"tree/min_height\":\"Find the minimum depth of a binary tree\",\"tree/pretty_print\":\"Print a dictionary-based tree in a readable arrow format\",\"tree/tree\":\"Re-export TreeNode class for backward compatibility\",\"tree/bst_predecessor\":\"Find the inorder predecessor of a node in a BST\",\"tree/bst_successor\":\"Find the inorder successor of a node in a BST\",\"tree/traversal_level_order\":\"Traverse a binary tree level by level left to right\",\"tree/traversal_zigzag\":\"Traverse a binary tree in zigzag (alternating) level order\",\"tree/binary_tree_views\":\"Compute left, right, top, and bottom views of a binary tree\"};\n\nconst catDirs={\"Array\":\"array\",\"Backtracking\":\"backtracking\",\"Bit Manipulation\":\"bit_manipulation\",\"Compression\":\"compression\",\"Data Structures\":\"data_structures\",\"Dynamic Programming\":\"dynamic_programming\",\"Graph\":\"graph\",\"Greedy\":\"greedy\",\"Heap\":\"heap\",\"Linked List\":\"linked_list\",\"Map\":\"map\",\"Math\":\"math\",\"Matrix\":\"matrix\",\"Queue\":\"queue\",\"Searching\":\"searching\",\"Set\":\"set\",\"Sorting\":\"sorting\",\"Stack\":\"stack\",\"Streaming\":\"streaming\",\"String\":\"string\",\"Tree\":\"tree\"};\n\nconst mainEl=document.getElementById(\"main\");\nconst searchInput=document.getElementById(\"search\");\nconst statsEl=document.getElementById(\"stats\");\nconst noResults=document.getElementById(\"no-results\");\nconst backdrop=document.getElementById(\"modal-backdrop\");\nconst modalTitle=document.getElementById(\"modal-title\");\nconst modalCat=document.getElementById(\"modal-cat\");\nconst modalBody=document.getElementById(\"modal-body\");\n\n// Source cache\nconst cache=new Map();\n\nfunction fmt(s){return s.replace(/_/g,\" \")}\nfunction esc(s){return s.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\")}\nfunction getDesc(dir,a){return DESC[dir+\"/\"+a]||\"\"}\n\n// Parse source into {mechanism, code, examples, funcName, isClass}\nfunction parseSource(src){\n let mechanism=\"\",code=src,examples=[],funcName=\"\",isClass=false;\n // Extract module docstring\n const m=src.match(/^(\"\"\"|''')([\\s\\S]*?)\\1/);\n if(m){\n  mechanism=m[2].trim();\n  code=src.slice(m.index+m[0].length).trim();\n  code=code.replace(/^from __future__ import annotations\\s*/,\"\");\n }\n // Find first public function or class name\n const cm=code.match(/^class\\s+(\\w+)/m);\n const fm=code.match(/^def\\s+(\\w+)/m);\n if(cm && (!fm || code.indexOf(cm[0])<code.indexOf(fm[0]))){\n  funcName=cm[1]; isClass=true;\n } else if(fm){\n  funcName=fm[1];\n }\n // Extract >>> examples from any docstring\n const docstrings=[...src.matchAll(/\"\"\"([\\s\\S]*?)\"\"\"/g)];\n for(const ds of docstrings){\n  const lines=ds[1].split(\"\\n\");\n  let ex=[];\n  for(const l of lines){\n   const t=l.trim();\n   if(t.startsWith(\">>>\")){\n    ex.push(t);\n   } else if(t.startsWith(\"...\")){\n    ex.push(t);\n   } else if(ex.length && t){\n    ex.push(\"# \"+t);\n   }\n  }\n  if(ex.length){examples.push(ex.join(\"\\n\"));break;}\n }\n // Detect re-export files (just imports + __all__)\n const isReexport=/^(from\\s|import\\s|__all__\\s*=|#|\\s*$)/m.test(code) && code.split(\"\\n\").every(l=>/^\\s*$|^from\\s|^import\\s|^__all__\\s*=|^#/.test(l));\n return {mechanism,code,examples,funcName,isClass,isReexport};\n}\n\nasync function openModal(cat,dir,algo){\n const label=fmt(algo);\n const desc=getDesc(dir,algo);\n modalTitle.textContent=label;\n modalCat.textContent=cat;\n backdrop.classList.add(\"open\");\n document.body.style.overflow=\"hidden\";\n\n const href=`${REPO}/${dir}/${algo}.py`;\n const rawUrl=`${RAW}/${dir}/${algo}.py`;\n\n // Show loading\n modalBody.innerHTML=`<div class=\"section-label\">Description</div><div class=\"mechanism\">${esc(desc)}</div><div class=\"section-label\">Source</div><div class=\"loading\">Loading...</div>`;\n\n let src=cache.get(rawUrl);\n if(!src){\n  try{\n   const r=await fetch(rawUrl);\n   src=await r.text();\n   cache.set(rawUrl,src);\n  }catch(e){\n   modalBody.innerHTML=`<div class=\"section-label\">Description</div><div class=\"mechanism\">${esc(desc)}</div><div class=\"section-label\">Source</div><p>Failed to load source. <a href=\"${href}\" target=\"_blank\" rel=\"noopener\">View on GitHub</a></p>`;\n   return;\n  }\n }\n\n const {mechanism,code,examples,funcName,isClass,isReexport}=parseSource(src);\n\n let html=\"\";\n\n if(isReexport){\n  // Re-export file: just show description + code, no usage/mechanism\n  html+=`<div class=\"section-label\">Description</div><div class=\"mechanism\">${esc(desc)}</div>`;\n  html+=`<div class=\"section-label\">Code</div><pre><code>${esc(code)}</code></pre>`;\n } else {\n  // Build usage snippet\n  let usage=`pip install algorithms\\n`;\n  if(isClass){\n   usage+=`from algorithms.${dir} import ${funcName}\\n\\n`;\n   if(examples.length){\n    usage+=examples[0].replace(/^>>> /gm,\"\").replace(/^\\.\\.\\. /gm,\"\").replace(/^# /gm,\"# => \");\n   } else {\n    usage+=`obj = ${funcName}(...)\\n# see source for constructor args`;\n   }\n  } else if(funcName){\n   usage+=`from algorithms.${dir} import ${funcName}\\n\\n`;\n   if(examples.length){\n    usage+=examples[0].replace(/^>>> /gm,\"\").replace(/^\\.\\.\\. /gm,\"\").replace(/^# /gm,\"# => \");\n   } else {\n    usage+=`result = ${funcName}(...)\\n# see source for arguments`;\n   }\n  }\n\n  if(funcName) html+=`<div class=\"section-label\">Usage</div><pre><code>${esc(usage)}</code></pre>`;\n  if(mechanism) html+=`<div class=\"section-label\">Mechanism</div><div class=\"mechanism\">${esc(mechanism)}</div>`;\n  html+=`<div class=\"section-label\">Code</div><pre><code>${esc(code)}</code></pre>`;\n }\n\n html+=`<a class=\"src-link\" href=\"${href}\" target=\"_blank\" rel=\"noopener\">View on GitHub &rarr;</a>`;\n modalBody.innerHTML=html;\n}\n\nfunction closeModal(){\n backdrop.classList.remove(\"open\");\n document.body.style.overflow=\"\";\n}\n\ndocument.getElementById(\"modal-close\").onclick=closeModal;\nbackdrop.onclick=e=>{if(e.target===backdrop)closeModal()};\ndocument.addEventListener(\"keydown\",e=>{if(e.key===\"Escape\")closeModal()});\n// Stop clicks inside modal from closing it\ndocument.getElementById(\"modal\").onclick=e=>e.stopPropagation();\n\nfunction render(q=\"\"){\n const lq=q.toLowerCase().trim();\n mainEl.innerHTML=\"\";\n let shown=0,cats=0;\n for(const[cat,algos]of Object.entries(DATA)){\n  const dir=catDirs[cat];\n  const matched=lq?algos.filter(a=>{\n   const name=a.replaceAll(\"_\",\" \");\n   const desc=getDesc(dir,a).toLowerCase();\n   return name.includes(lq)||cat.toLowerCase().includes(lq)||desc.includes(lq);\n  }):algos;\n  if(!matched.length)continue;\n  cats++;\n  shown+=matched.length;\n  const sec=document.createElement(\"div\");\n  sec.className=\"category\";\n  const hdr=document.createElement(\"div\");\n  hdr.className=\"cat-header\";\n  hdr.innerHTML=`<span>${cat} <span class=\"count\">${matched.length}</span></span><span class=\"arrow\">&#9660;</span>`;\n  const list=document.createElement(\"div\");\n  list.className=\"algo-list\";\n  for(const a of matched){\n   const d=document.createElement(\"div\");\n   d.className=\"algo\";\n   const label=fmt(a);\n   const desc=getDesc(dir,a);\n   if(lq){\n    const re=new RegExp(`(${lq.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$&')})`,\"gi\");\n    d.innerHTML=label.replace(re,\"<mark>$1</mark>\")+`<span class=\"desc\">${desc}</span>`;\n   }else{\n    d.textContent=label;\n   }\n   d.onclick=()=>openModal(cat,dir,a);\n   list.appendChild(d);\n  }\n  hdr.onclick=()=>{hdr.classList.toggle(\"collapsed\");list.classList.toggle(\"hidden\")};\n  sec.appendChild(hdr);\n  sec.appendChild(list);\n  mainEl.appendChild(sec);\n }\n noResults.style.display=shown?\"none\":\"block\";\n statsEl.textContent=lq?`${shown} result${shown!==1?\"s\":\"\"} in ${cats} categor${cats!==1?\"ies\":\"y\"}`:\"\";\n}\n\nsearchInput.addEventListener(\"input\",e=>render(e.target.value));\nrender();\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools>=68.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"algorithms\"\nversion = \"1.0.1\"\ndescription = \"Pythonic Data Structures and Algorithms\"\nreadme = \"README.md\"\nlicense = \"MIT\"\nrequires-python = \">=3.10\"\nauthors = [\n    { name = \"Algorithms Team & Contributors\", email = \"kwk236@gmail.com\" },\n]\nclassifiers = [\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Programming Language :: Python :: 3.12\",\n    \"Programming Language :: Python :: 3.13\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n    \"Topic :: Education\",\n]\n\n[project.urls]\nHomepage = \"https://github.com/keon/algorithms\"\nRepository = \"https://github.com/keon/algorithms\"\n\n[project.optional-dependencies]\ndev = [\n    \"pytest\",\n    \"ruff\",\n    \"mypy\",\n    \"black\",\n]\n\n[tool.setuptools.packages.find]\nexclude = [\"tests\", \"tests.*\", \"tools\", \"tools.*\", \"docs\", \"docs.*\"]\n\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\naddopts = [\"-v\", \"--tb=short\"]\n\n[tool.ruff]\nline-length = 88\ntarget-version = \"py310\"\n\n[tool.ruff.lint]\nselect = [\"E\", \"W\", \"F\", \"I\", \"N\", \"UP\", \"B\", \"SIM\"]\n\n[tool.ruff.lint.isort]\nknown-first-party = [\"algorithms\"]\n\n[tool.mypy]\npython_version = \"3.10\"\nwarn_return_any = true\ncheck_untyped_defs = true\ndisallow_untyped_defs = false\n\n[tool.black]\nline-length = 88\ntarget-version = [\"py310\"]\n"
  },
  {
    "path": "tests/test_array.py",
    "content": "import unittest\n\nfrom algorithms.array import (\n    Interval,\n    delete_nth,\n    delete_nth_naive,\n    flatten,\n    flatten_iter,\n    garage,\n    get_longest_non_repeat_v1,\n    get_longest_non_repeat_v2,\n    josephus,\n    limit,\n    longest_non_repeat_v1,\n    longest_non_repeat_v2,\n    max_ones_index,\n    merge_intervals,\n    missing_ranges,\n    move_zeros,\n    n_sum,\n    plus_one_v1,\n    plus_one_v2,\n    plus_one_v3,\n    remove_duplicates,\n    rotate_v1,\n    rotate_v2,\n    rotate_v3,\n    summarize_ranges,\n    three_sum,\n    top_1,\n    trimmean,\n    two_sum,\n)\n\n\nclass TestJosephus(unittest.TestCase):\n    def test_josephus(self):\n\n        a = [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"]\n        josephus_generator = josephus(a, 3)\n        self.assertEqual(next(josephus_generator), \"3\")\n        self.assertEqual(next(josephus_generator), \"6\")\n        self.assertEqual(next(josephus_generator), \"9\")\n        self.assertEqual(next(josephus_generator), \"4\")\n        self.assertEqual(next(josephus_generator), \"8\")\n        self.assertEqual(next(josephus_generator), \"5\")\n        self.assertEqual(next(josephus_generator), \"2\")\n        self.assertEqual(next(josephus_generator), \"7\")\n        self.assertEqual(next(josephus_generator), \"1\")\n        self.assertRaises(StopIteration, next, josephus_generator)\n\n\nclass TestDeleteNth(unittest.TestCase):\n    def test_delete_nth_naive(self):\n\n        self.assertListEqual(\n            delete_nth_naive([20, 37, 20, 21, 37, 21, 21], n=1), [20, 37, 21]\n        )\n        self.assertListEqual(\n            delete_nth_naive([1, 1, 3, 3, 7, 2, 2, 2, 2], n=3), [1, 1, 3, 3, 7, 2, 2, 2]\n        )\n        self.assertListEqual(\n            delete_nth_naive([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=3),\n            [1, 2, 3, 1, 1, 2, 2, 3, 3, 4, 5],\n        )\n        self.assertListEqual(delete_nth_naive([], n=5), [])\n        self.assertListEqual(\n            delete_nth_naive([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=0), []\n        )\n\n    def test_delete_nth(self):\n\n        self.assertListEqual(\n            delete_nth([20, 37, 20, 21, 37, 21, 21], n=1), [20, 37, 21]\n        )\n        self.assertListEqual(\n            delete_nth([1, 1, 3, 3, 7, 2, 2, 2, 2], n=3), [1, 1, 3, 3, 7, 2, 2, 2]\n        )\n        self.assertListEqual(\n            delete_nth([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=3),\n            [1, 2, 3, 1, 1, 2, 2, 3, 3, 4, 5],\n        )\n        self.assertListEqual(delete_nth([], n=5), [])\n        self.assertListEqual(\n            delete_nth([1, 2, 3, 1, 1, 2, 1, 2, 3, 3, 2, 4, 5, 3, 1], n=0), []\n        )\n\n\nclass TestFlatten(unittest.TestCase):\n    def test_flatten(self):\n\n        nested_list = [2, 1, [3, [4, 5], 6], 7, [8]]\n        flattened = flatten(nested_list)\n        self.assertEqual(flattened, [2, 1, 3, 4, 5, 6, 7, 8])\n\n        nested_list = [[3, [4, 5], 6], 7, [8]]\n        flattened = flatten(nested_list)\n        self.assertEqual(flattened, [3, 4, 5, 6, 7, 8])\n\n        nested_list = [[], [8]]\n        flattened = flatten(nested_list)\n        self.assertEqual(flattened, [8])\n\n    def test_flatten_iter(self):\n\n        nested_list = [2, 1, [3, [4, 5], 6], 7, [8]]\n        flattened = flatten_iter(nested_list)\n        self.assertEqual(next(flattened), 2)\n        self.assertEqual(next(flattened), 1)\n        self.assertEqual(next(flattened), 3)\n        self.assertEqual(next(flattened), 4)\n        self.assertEqual(next(flattened), 5)\n        self.assertEqual(next(flattened), 6)\n        self.assertEqual(next(flattened), 7)\n        self.assertEqual(next(flattened), 8)\n        self.assertRaises(StopIteration, next, flattened)\n\n        nested_list = [[3, [4, 5], 6], 7, [8]]\n        flattened = flatten_iter(nested_list)\n        self.assertEqual(next(flattened), 3)\n        self.assertEqual(next(flattened), 4)\n        self.assertEqual(next(flattened), 5)\n        self.assertEqual(next(flattened), 6)\n        self.assertEqual(next(flattened), 7)\n        self.assertEqual(next(flattened), 8)\n        self.assertRaises(StopIteration, next, flattened)\n\n        nested_list = [[], [8]]\n        flattened = flatten_iter(nested_list)\n        self.assertEqual(next(flattened), 8)\n        self.assertRaises(StopIteration, next, flattened)\n\n\nclass TestGarage(unittest.TestCase):\n    def test_garage(self):\n\n        initial = [1, 2, 3, 0, 4]\n        final = [0, 3, 2, 1, 4]\n        steps, seq = garage(initial, final)\n\n        self.assertEqual(steps, 4)\n        self.assertListEqual(\n            seq, [[0, 2, 3, 1, 4], [2, 0, 3, 1, 4], [2, 3, 0, 1, 4], [0, 3, 2, 1, 4]]\n        )\n\n\nclass TestLongestNonRepeat(unittest.TestCase):\n    def test_longest_non_repeat_v1(self):\n\n        string = \"abcabcbb\"\n        self.assertEqual(longest_non_repeat_v1(string), 3)\n\n        string = \"bbbbb\"\n        self.assertEqual(longest_non_repeat_v1(string), 1)\n\n        string = \"pwwkew\"\n        self.assertEqual(longest_non_repeat_v1(string), 3)\n\n        string = \"dvdf\"\n        self.assertEqual(longest_non_repeat_v1(string), 3)\n\n        string = \"asjrgapa\"\n        self.assertEqual(longest_non_repeat_v1(string), 6)\n\n    def test_longest_non_repeat_v2(self):\n\n        string = \"abcabcbb\"\n        self.assertEqual(longest_non_repeat_v2(string), 3)\n\n        string = \"bbbbb\"\n        self.assertEqual(longest_non_repeat_v2(string), 1)\n\n        string = \"pwwkew\"\n        self.assertEqual(longest_non_repeat_v2(string), 3)\n\n        string = \"dvdf\"\n        self.assertEqual(longest_non_repeat_v2(string), 3)\n\n        string = \"asjrgapa\"\n        self.assertEqual(longest_non_repeat_v2(string), 6)\n\n    def test_get_longest_non_repeat_v1(self):\n        string = \"abcabcbb\"\n        self.assertEqual(get_longest_non_repeat_v1(string), (3, \"abc\"))\n\n        string = \"bbbbb\"\n        self.assertEqual(get_longest_non_repeat_v1(string), (1, \"b\"))\n\n        string = \"pwwkew\"\n        self.assertEqual(get_longest_non_repeat_v1(string), (3, \"wke\"))\n\n        string = \"dvdf\"\n        self.assertEqual(get_longest_non_repeat_v1(string), (3, \"vdf\"))\n\n        string = \"asjrgapa\"\n        self.assertEqual(get_longest_non_repeat_v1(string), (6, \"sjrgap\"))\n\n    def test_get_longest_non_repeat_v2(self):\n        string = \"abcabcbb\"\n        self.assertEqual(get_longest_non_repeat_v2(string), (3, \"abc\"))\n\n        string = \"bbbbb\"\n        self.assertEqual(get_longest_non_repeat_v2(string), (1, \"b\"))\n\n        string = \"pwwkew\"\n        self.assertEqual(get_longest_non_repeat_v2(string), (3, \"wke\"))\n\n        string = \"dvdf\"\n        self.assertEqual(get_longest_non_repeat_v2(string), (3, \"vdf\"))\n\n        string = \"asjrgapa\"\n        self.assertEqual(get_longest_non_repeat_v2(string), (6, \"sjrgap\"))\n\n\nclass TestMaxOnesIndex(unittest.TestCase):\n    def test_max_ones_index(self):\n\n        self.assertEqual(9, max_ones_index([1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1]))\n        self.assertEqual(3, max_ones_index([1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1]))\n        self.assertEqual(-1, max_ones_index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]))\n\n\nclass TestMergeInterval(unittest.TestCase):\n    def test_merge(self):\n        interval_list = [[1, 3], [2, 6], [8, 10], [15, 18]]\n        intervals = [Interval(i[0], i[1]) for i in interval_list]\n        merged_intervals = Interval.merge(intervals)\n        self.assertEqual(\n            merged_intervals, [Interval(1, 6), Interval(8, 10), Interval(15, 18)]\n        )\n\n    def test_merge_intervals(self):\n        interval_list = [[1, 3], [2, 6], [8, 10], [15, 18]]\n        merged_intervals = merge_intervals(interval_list)\n        self.assertEqual(merged_intervals, [[1, 6], [8, 10], [15, 18]])\n\n\nclass TestMissingRanges(unittest.TestCase):\n    def test_missing_ranges(self):\n\n        arr = [3, 5, 10, 11, 12, 15, 19]\n\n        self.assertListEqual(\n            missing_ranges(arr, 0, 20),\n            [(0, 2), (4, 4), (6, 9), (13, 14), (16, 18), (20, 20)],\n        )\n\n        self.assertListEqual(\n            missing_ranges(arr, 6, 100), [(6, 9), (13, 14), (16, 18), (20, 100)]\n        )\n\n\nclass TestMoveZeros(unittest.TestCase):\n    def test_move_zeros(self):\n\n        self.assertListEqual(\n            move_zeros([False, 1, 0, 1, 2, 0, 1, 3, \"a\"]),\n            [False, 1, 1, 2, 1, 3, \"a\", 0, 0],\n        )\n\n        self.assertListEqual(\n            move_zeros([0, 34, \"rahul\", [], None, 0, True, 0]),\n            [34, \"rahul\", [], None, True, 0, 0, 0],\n        )\n\n\nclass TestPlusOne(unittest.TestCase):\n    def test_plus_one_v1(self):\n\n        self.assertListEqual(plus_one_v1([0]), [1])\n        self.assertListEqual(plus_one_v1([9]), [1, 0])\n        self.assertListEqual(plus_one_v1([1, 0, 9]), [1, 1, 0])\n        self.assertListEqual(plus_one_v1([9, 9, 8, 0, 0, 9]), [9, 9, 8, 0, 1, 0])\n        self.assertListEqual(plus_one_v1([9, 9, 9, 9]), [1, 0, 0, 0, 0])\n\n    def test_plus_one_v2(self):\n\n        self.assertListEqual(plus_one_v2([0]), [1])\n        self.assertListEqual(plus_one_v2([9]), [1, 0])\n        self.assertListEqual(plus_one_v2([1, 0, 9]), [1, 1, 0])\n        self.assertListEqual(plus_one_v2([9, 9, 8, 0, 0, 9]), [9, 9, 8, 0, 1, 0])\n        self.assertListEqual(plus_one_v2([9, 9, 9, 9]), [1, 0, 0, 0, 0])\n\n    def test_plus_one_v3(self):\n\n        self.assertListEqual(plus_one_v3([0]), [1])\n        self.assertListEqual(plus_one_v3([9]), [1, 0])\n        self.assertListEqual(plus_one_v3([1, 0, 9]), [1, 1, 0])\n        self.assertListEqual(plus_one_v3([9, 9, 8, 0, 0, 9]), [9, 9, 8, 0, 1, 0])\n        self.assertListEqual(plus_one_v3([9, 9, 9, 9]), [1, 0, 0, 0, 0])\n\n\nclass TestRemoveDuplicate(unittest.TestCase):\n    def test_remove_duplicates(self):\n        self.assertListEqual(\n            remove_duplicates(\n                [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 7, 7, 8, 8, 9, 10, 10]\n            ),\n            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n        )\n        self.assertListEqual(\n            remove_duplicates([\"hey\", \"hello\", \"hello\", \"car\", \"house\", \"house\"]),\n            [\"hey\", \"hello\", \"car\", \"house\"],\n        )\n        self.assertListEqual(\n            remove_duplicates([True, True, False, True, False, None, None]),\n            [True, False, None],\n        )\n        self.assertListEqual(\n            remove_duplicates([1, 1, \"hello\", \"hello\", True, False, False]),\n            [1, \"hello\", False],\n        )\n        self.assertListEqual(\n            remove_duplicates([1, \"hello\", True, False]), [1, \"hello\", False]\n        )\n\n\nclass TestRotateArray(unittest.TestCase):\n    def test_rotate_v1(self):\n\n        self.assertListEqual(\n            rotate_v1([1, 2, 3, 4, 5, 6, 7], k=3), [5, 6, 7, 1, 2, 3, 4]\n        )\n        self.assertListEqual(\n            rotate_v1([1, 2, 3, 4, 5, 6, 7], k=1), [7, 1, 2, 3, 4, 5, 6]\n        )\n        self.assertListEqual(\n            rotate_v1([1, 2, 3, 4, 5, 6, 7], k=7), [1, 2, 3, 4, 5, 6, 7]\n        )\n        self.assertListEqual(rotate_v1([1, 2], k=111), [2, 1])\n\n    def test_rotate_v2(self):\n\n        self.assertListEqual(\n            rotate_v2([1, 2, 3, 4, 5, 6, 7], k=3), [5, 6, 7, 1, 2, 3, 4]\n        )\n        self.assertListEqual(\n            rotate_v2([1, 2, 3, 4, 5, 6, 7], k=1), [7, 1, 2, 3, 4, 5, 6]\n        )\n        self.assertListEqual(\n            rotate_v2([1, 2, 3, 4, 5, 6, 7], k=7), [1, 2, 3, 4, 5, 6, 7]\n        )\n        self.assertListEqual(rotate_v2([1, 2], k=111), [2, 1])\n\n    def test_rotate_v3(self):\n\n        self.assertListEqual(\n            rotate_v3([1, 2, 3, 4, 5, 6, 7], k=3), [5, 6, 7, 1, 2, 3, 4]\n        )\n        self.assertListEqual(\n            rotate_v3([1, 2, 3, 4, 5, 6, 7], k=1), [7, 1, 2, 3, 4, 5, 6]\n        )\n        self.assertListEqual(\n            rotate_v3([1, 2, 3, 4, 5, 6, 7], k=7), [1, 2, 3, 4, 5, 6, 7]\n        )\n        self.assertListEqual(rotate_v3([1, 2], k=111), [2, 1])\n\n\nclass TestSummaryRanges(unittest.TestCase):\n    def test_summarize_ranges(self):\n\n        self.assertListEqual(\n            summarize_ranges([0, 1, 2, 4, 5, 7]), [(0, 2), (4, 5), (7, 7)]\n        )\n        self.assertListEqual(\n            summarize_ranges([-5, -4, -3, 1, 2, 4, 5, 6]), [(-5, -3), (1, 2), (4, 6)]\n        )\n        self.assertListEqual(summarize_ranges([-2, -1, 0, 1, 2]), [(-2, 2)])\n\n\nclass TestThreeSum(unittest.TestCase):\n    def test_three_sum(self):\n\n        self.assertSetEqual(three_sum([-1, 0, 1, 2, -1, -4]), {(-1, 0, 1), (-1, -1, 2)})\n\n        self.assertSetEqual(\n            three_sum([-1, 3, 1, 2, -1, -4, -2]), {(-4, 1, 3), (-2, -1, 3), (-1, -1, 2)}\n        )\n\n\nclass TestTwoSum(unittest.TestCase):\n    def test_two_sum(self):\n\n        self.assertTupleEqual((0, 2), two_sum([2, 11, 7, 9], target=9))\n        self.assertTupleEqual((0, 3), two_sum([-3, 5, 2, 3, 8, -9], target=0))\n\n        self.assertIsNone(two_sum([-3, 5, 2, 3, 8, -9], target=6))\n\n\nclass TestTrimmean(unittest.TestCase):\n    def test_trimmean(self):\n\n        self.assertEqual(trimmean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 20), 5.5)\n        self.assertEqual(trimmean([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 20), 6.0)\n\n\nclass TestTop1(unittest.TestCase):\n    def test_top_1(self):\n        self.assertListEqual(top_1([1, 1, 2, 2, 3]), [1, 2])\n        self.assertListEqual(top_1([1, 2, 3, 324, 234, 23, 23, 1, 23, 23]), [23])\n\n\nclass TestLimit(unittest.TestCase):\n    def test_limit(self):\n        self.assertListEqual(limit([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])\n        self.assertListEqual(limit([1, 2, 3, 4, 5], 2, 4), [2, 3, 4])\n        self.assertListEqual(limit([1, 2, 3, 4, 5], 2), [2, 3, 4, 5])\n        self.assertListEqual(limit([1, 2, 3, 4, 5], None, 4), [1, 2, 3, 4])\n\n\nclass TestNSum(unittest.TestCase):\n    def test_n_sum(self):\n        self.assertEqual(n_sum(2, [-3, 5, 2, 3, 8, -9], 6), [])  # noqa: E501\n        self.assertEqual(\n            n_sum(3, [-5, -4, -3, -2, -1, 0, 1, 2, 3], 0),\n            sorted(\n                [\n                    [-5, 2, 3],\n                    [-2, 0, 2],\n                    [-4, 1, 3],\n                    [-3, 1, 2],\n                    [-1, 0, 1],\n                    [-2, -1, 3],\n                    [-3, 0, 3],\n                ]\n            ),\n        )  # noqa: E501\n        self.assertEqual(\n            n_sum(3, [-1, 0, 1, 2, -1, -4], 0), sorted([[-1, -1, 2], [-1, 0, 1]])\n        )  # noqa: E501\n        self.assertEqual(\n            n_sum(4, [1, 0, -1, 0, -2, 2], 0),\n            sorted([[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]),\n        )  # noqa: E501\n        self.assertEqual(\n            n_sum(\n                4, [7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, -3, -2], 10\n            ),\n            sorted(\n                [\n                    [-6, 2, 7, 7],\n                    [-6, 3, 6, 7],\n                    [-6, 4, 5, 7],\n                    [-6, 4, 6, 6],\n                    [-5, 1, 7, 7],\n                    [-5, 2, 6, 7],\n                    [-5, 3, 5, 7],\n                    [-5, 3, 6, 6],\n                    [-5, 4, 4, 7],\n                    [-5, 4, 5, 6],\n                    [-4, 0, 7, 7],\n                    [-4, 1, 6, 7],\n                    [-4, 2, 5, 7],\n                    [-4, 2, 6, 6],\n                    [-4, 3, 4, 7],\n                    [-4, 3, 5, 6],\n                    [-4, 4, 4, 6],\n                    [-3, -1, 7, 7],\n                    [-3, 0, 6, 7],\n                    [-3, 1, 5, 7],\n                    [-3, 1, 6, 6],\n                    [-3, 2, 4, 7],\n                    [-3, 2, 5, 6],\n                    [-3, 3, 4, 6],\n                    [-3, 4, 4, 5],\n                    [-2, -2, 7, 7],\n                    [-2, -1, 6, 7],\n                    [-2, 0, 5, 7],\n                    [-2, 0, 6, 6],\n                    [-2, 1, 4, 7],\n                    [-2, 1, 5, 6],\n                    [-2, 2, 3, 7],\n                    [-2, 2, 4, 6],\n                    [-2, 3, 4, 5],\n                    [-1, 0, 4, 7],\n                    [-1, 0, 5, 6],\n                    [-1, 1, 3, 7],\n                    [-1, 1, 4, 6],\n                    [-1, 2, 3, 6],\n                    [-1, 2, 4, 5],\n                    [-1, 3, 4, 4],\n                    [0, 1, 2, 7],\n                    [0, 1, 3, 6],\n                    [0, 1, 4, 5],\n                    [0, 2, 3, 5],\n                    [0, 2, 4, 4],\n                    [1, 2, 3, 4],\n                ]\n            ),\n        )  # noqa: E501\n\n        self.assertEqual(\n            n_sum(\n                2,\n                [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]],\n                0,  # noqa: E501\n                sum_closure=lambda a, b: a[0] + b[0],\n            ),  # noqa: E501\n            [[[-3, 0], [3, 3]], [[-2, 1], [2, 2]]],\n        )  # noqa: E501\n        self.assertEqual(\n            n_sum(\n                2,\n                [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]],\n                [0, 3],  # noqa: E501\n                sum_closure=lambda a, b: [a[0] + b[0], a[1] + b[1]],  # noqa: E501\n                same_closure=lambda a, b: a[0] == b[0] and a[1] == b[1],\n            ),  # noqa: E501\n            [[[-3, 0], [3, 3]], [[-2, 1], [2, 2]]],\n        )  # noqa: E501\n        self.assertEqual(\n            n_sum(\n                2,\n                [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]],\n                -5,  # noqa: E501\n                sum_closure=lambda a, b: [a[0] + b[1], a[1] + b[0]],  # noqa: E501\n                compare_closure=lambda a, b: -1 if a[0] < b else 1 if a[0] > b else 0,\n            ),  # noqa: E501\n            [[[-9, 5], [8, 4]]],\n        )  # noqa: E501\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_backtracking.py",
    "content": "import unittest\n\nfrom algorithms.backtracking import (\n    add_operators,\n    anagram,\n    array_sum_combinations,\n    combination_sum,\n    find_words,\n    generate_abbreviations,\n    generate_parenthesis_v1,\n    generate_parenthesis_v2,\n    get_factors,\n    letter_combinations,\n    palindromic_substrings,\n    pattern_match,\n    permute,\n    permute_iter,\n    permute_recursive,\n    permute_unique,\n    recursive_get_factors,\n    subsets,\n    subsets_unique,\n    subsets_v2,\n    unique_array_sum_combinations,\n)\n\n\nclass TestAddOperator(unittest.TestCase):\n    def test_add_operators(self):\n        # \"123\", 6 -> [\"1+2+3\", \"1*2*3\"]\n        s = \"123\"\n        target = 6\n        self.assertEqual(add_operators(s, target), [\"1+2+3\", \"1*2*3\"])\n        # \"232\", 8 -> [\"2*3+2\", \"2+3*2\"]\n        s = \"232\"\n        target = 8\n        self.assertEqual(add_operators(s, target), [\"2+3*2\", \"2*3+2\"])\n\n        s = \"123045\"\n        target = 3\n        answer = [\n            \"1+2+3*0*4*5\",\n            \"1+2+3*0*45\",\n            \"1+2-3*0*4*5\",\n            \"1+2-3*0*45\",\n            \"1-2+3+0-4+5\",\n            \"1-2+3-0-4+5\",\n            \"1*2+3*0-4+5\",\n            \"1*2-3*0-4+5\",\n            \"1*23+0-4*5\",\n            \"1*23-0-4*5\",\n            \"12+3*0-4-5\",\n            \"12-3*0-4-5\",\n        ]\n        self.assertEqual(add_operators(s, target), answer)\n\n\nclass TestPermuteAndAnagram(unittest.TestCase):\n    def test_permute(self):\n        perms = [\"abc\", \"bac\", \"bca\", \"acb\", \"cab\", \"cba\"]\n        self.assertEqual(perms, permute(\"abc\"))\n\n    def test_permute_iter(self):\n        it = permute_iter(\"abc\")\n        perms = [\"abc\", \"bac\", \"bca\", \"acb\", \"cab\", \"cba\"]\n        for i in range(len(perms)):\n            self.assertEqual(perms[i], next(it))\n\n    def test_angram(self):\n        self.assertTrue(anagram(\"apple\", \"pleap\"))\n        self.assertFalse(anagram(\"apple\", \"cherry\"))\n\n\nclass TestArrayCombinationSum(unittest.TestCase):\n    def test_array_sum_combinations(self):\n        a = [1, 2, 3, 3]\n        b = [2, 3, 3, 4]\n        c = [2, 3, 3, 4]\n        target = 7\n        answer = [\n            [1, 2, 4],\n            [1, 3, 3],\n            [1, 3, 3],\n            [1, 3, 3],\n            [1, 3, 3],\n            [1, 4, 2],\n            [2, 2, 3],\n            [2, 2, 3],\n            [2, 3, 2],\n            [2, 3, 2],\n            [3, 2, 2],\n            [3, 2, 2],\n        ]\n        answer.sort()\n        self.assertListEqual(sorted(array_sum_combinations(a, b, c, target)), answer)\n\n    def test_unique_array_sum_combinations(self):\n        a = [1, 2, 3, 3]\n        b = [2, 3, 3, 4]\n        c = [2, 3, 3, 4]\n        target = 7\n        answer = [(2, 3, 2), (3, 2, 2), (1, 2, 4), (1, 4, 2), (2, 2, 3), (1, 3, 3)]\n        answer.sort()\n        self.assertListEqual(\n            sorted(unique_array_sum_combinations(a, b, c, target)), answer\n        )\n\n\nclass TestCombinationSum(unittest.TestCase):\n    def check_sum(self, nums, target):\n        if sum(nums) == target:\n            return (True, nums)\n        else:\n            return (False, nums)\n\n    def test_combination_sum(self):\n        candidates1 = [2, 3, 6, 7]\n        target1 = 7\n        answer1 = [[2, 2, 3], [7]]\n        self.assertEqual(combination_sum(candidates1, target1), answer1)\n\n        candidates2 = [2, 3, 5]\n        target2 = 8\n        answer2 = [[2, 2, 2, 2], [2, 3, 3], [3, 5]]\n        self.assertEqual(combination_sum(candidates2, target2), answer2)\n\n\nclass TestFactorCombinations(unittest.TestCase):\n    def test_get_factors(self):\n        target1 = 32\n        answer1 = [[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]]\n        self.assertEqual(sorted(get_factors(target1)), sorted(answer1))\n\n        target2 = 12\n        answer2 = [[2, 6], [2, 2, 3], [3, 4]]\n        self.assertEqual(sorted(get_factors(target2)), sorted(answer2))\n        self.assertEqual(sorted(get_factors(1)), [])\n        self.assertEqual(sorted(get_factors(37)), [])\n\n    def test_recursive_get_factors(self):\n        target1 = 32\n        answer1 = [[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]]\n        self.assertEqual(sorted(recursive_get_factors(target1)), sorted(answer1))\n\n        target2 = 12\n        answer2 = [[2, 6], [2, 2, 3], [3, 4]]\n        self.assertEqual(sorted(recursive_get_factors(target2)), sorted(answer2))\n        self.assertEqual(sorted(recursive_get_factors(1)), [])\n        self.assertEqual(sorted(recursive_get_factors(37)), [])\n\n\nclass TestFindWords(unittest.TestCase):\n    def test_normal(self):\n        board = [\n            [\"o\", \"a\", \"a\", \"n\"],\n            [\"e\", \"t\", \"a\", \"e\"],\n            [\"i\", \"h\", \"k\", \"r\"],\n            [\"i\", \"f\", \"l\", \"v\"],\n        ]\n\n        words = [\"oath\", \"pea\", \"eat\", \"rain\"]\n        result = find_words(board, words)\n        test_result = [\"oath\", \"eat\"]\n        self.assertEqual(sorted(result), sorted(test_result))\n\n    def test_none(self):\n        board = [\n            [\"o\", \"a\", \"a\", \"n\"],\n            [\"e\", \"t\", \"a\", \"e\"],\n            [\"i\", \"h\", \"k\", \"r\"],\n            [\"i\", \"f\", \"l\", \"v\"],\n        ]\n\n        words = [\"chicken\", \"nugget\", \"hello\", \"world\"]\n        self.assertEqual(find_words(board, words), [])\n\n    def test_empty(self):\n        board = []\n        words = []\n        self.assertEqual(find_words(board, words), [])\n\n    def test_uneven(self):\n        board = [[\"o\", \"a\", \"a\", \"n\"], [\"e\", \"t\", \"a\", \"e\"]]\n        words = [\"oath\", \"pea\", \"eat\", \"rain\"]\n        self.assertEqual(find_words(board, words), [\"eat\"])\n\n    def test_repeat(self):\n        board = [[\"a\", \"a\", \"a\"], [\"a\", \"a\", \"a\"], [\"a\", \"a\", \"a\"]]\n        words = [\"a\", \"aa\", \"aaa\", \"aaaa\", \"aaaaa\"]\n        self.assertTrue(len(find_words(board, words)) == 5)\n\n\nclass TestGenerateAbbreviations(unittest.TestCase):\n    def test_generate_abbreviations(self):\n        word1 = \"word\"\n        answer1 = [\n            \"word\",\n            \"wor1\",\n            \"wo1d\",\n            \"wo2\",\n            \"w1rd\",\n            \"w1r1\",\n            \"w2d\",\n            \"w3\",\n            \"1ord\",\n            \"1or1\",\n            \"1o1d\",\n            \"1o2\",\n            \"2rd\",\n            \"2r1\",\n            \"3d\",\n            \"4\",\n        ]\n        self.assertEqual(sorted(generate_abbreviations(word1)), sorted(answer1))\n\n        word2 = \"hello\"\n        answer2 = [\n            \"hello\",\n            \"hell1\",\n            \"hel1o\",\n            \"hel2\",\n            \"he1lo\",\n            \"he1l1\",\n            \"he2o\",\n            \"he3\",\n            \"h1llo\",\n            \"h1ll1\",\n            \"h1l1o\",\n            \"h1l2\",\n            \"h2lo\",\n            \"h2l1\",\n            \"h3o\",\n            \"h4\",\n            \"1ello\",\n            \"1ell1\",\n            \"1el1o\",\n            \"1el2\",\n            \"1e1lo\",\n            \"1e1l1\",\n            \"1e2o\",\n            \"1e3\",\n            \"2llo\",\n            \"2ll1\",\n            \"2l1o\",\n            \"2l2\",\n            \"3lo\",\n            \"3l1\",\n            \"4o\",\n            \"5\",\n        ]\n        self.assertEqual(sorted(generate_abbreviations(word2)), sorted(answer2))\n\n\nclass TestPatternMatch(unittest.TestCase):\n    def test_pattern_match(self):\n        pattern1 = \"abab\"\n        string1 = \"redblueredblue\"\n        pattern2 = \"aaaa\"\n        string2 = \"asdasdasdasd\"\n        pattern3 = \"aabb\"\n        string3 = \"xyzabcxzyabc\"\n\n        self.assertTrue(pattern_match(pattern1, string1))\n        self.assertTrue(pattern_match(pattern2, string2))\n        self.assertFalse(pattern_match(pattern3, string3))\n\n\nclass TestGenerateParenthesis(unittest.TestCase):\n    def test_generate_parenthesis(self):\n        self.assertEqual(generate_parenthesis_v1(2), [\"()()\", \"(())\"])\n        self.assertEqual(\n            generate_parenthesis_v1(3),\n            [\"()()()\", \"()(())\", \"(())()\", \"(()())\", \"((()))\"],\n        )\n        self.assertEqual(generate_parenthesis_v2(2), [\"(())\", \"()()\"])\n        self.assertEqual(\n            generate_parenthesis_v2(3),\n            [\"((()))\", \"(()())\", \"(())()\", \"()(())\", \"()()()\"],\n        )\n\n\nclass TestLetterCombinations(unittest.TestCase):\n    def test_letter_combinations(self):\n        digit1 = \"23\"\n        answer1 = [\"ad\", \"ae\", \"af\", \"bd\", \"be\", \"bf\", \"cd\", \"ce\", \"cf\"]\n        self.assertEqual(sorted(letter_combinations(digit1)), sorted(answer1))\n\n        digit2 = \"34\"\n        answer2 = [\"dg\", \"dh\", \"di\", \"eg\", \"eh\", \"ei\", \"fg\", \"fh\", \"fi\"]\n        self.assertEqual(sorted(letter_combinations(digit2)), sorted(answer2))\n\n\nclass TestPalindromicSubstrings(unittest.TestCase):\n    def test_palindromic_substrings(self):\n        string1 = \"abc\"\n        answer1 = [[\"a\", \"b\", \"c\"]]\n        self.assertEqual(palindromic_substrings(string1), sorted(answer1))\n\n        string2 = \"abcba\"\n        answer2 = [[\"abcba\"], [\"a\", \"bcb\", \"a\"], [\"a\", \"b\", \"c\", \"b\", \"a\"]]\n        self.assertEqual(sorted(palindromic_substrings(string2)), sorted(answer2))\n\n        string3 = \"abcccba\"\n        answer3 = [\n            [\"abcccba\"],\n            [\"a\", \"bcccb\", \"a\"],\n            [\"a\", \"b\", \"ccc\", \"b\", \"a\"],\n            [\"a\", \"b\", \"cc\", \"c\", \"b\", \"a\"],\n            [\"a\", \"b\", \"c\", \"cc\", \"b\", \"a\"],\n            [\"a\", \"b\", \"c\", \"c\", \"c\", \"b\", \"a\"],\n        ]\n        self.assertEqual(sorted(palindromic_substrings(string3)), sorted(answer3))\n\n\nclass TestPermuteUnique(unittest.TestCase):\n    def test_permute_unique(self):\n        nums1 = [1, 1, 2]\n        answer1 = [[2, 1, 1], [1, 2, 1], [1, 1, 2]]\n        self.assertEqual(sorted(permute_unique(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 1, 3]\n        answer2 = [\n            [3, 1, 2, 1],\n            [1, 3, 2, 1],\n            [1, 2, 3, 1],\n            [1, 2, 1, 3],\n            [3, 2, 1, 1],\n            [2, 3, 1, 1],\n            [2, 1, 3, 1],\n            [2, 1, 1, 3],\n            [3, 1, 1, 2],\n            [1, 3, 1, 2],\n            [1, 1, 3, 2],\n            [1, 1, 2, 3],\n        ]\n        self.assertEqual(sorted(permute_unique(nums2)), sorted(answer2))\n\n        nums3 = [1, 2, 3]\n        answer3 = [[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]\n        self.assertEqual(sorted(permute_unique(nums3)), sorted(answer3))\n\n\nclass TestPermute(unittest.TestCase):\n    def test_permute(self):\n        nums1 = [1, 2, 3, 4]\n        answer1 = [\n            [1, 2, 3, 4],\n            [2, 1, 3, 4],\n            [2, 3, 1, 4],\n            [2, 3, 4, 1],\n            [1, 3, 2, 4],\n            [3, 1, 2, 4],\n            [3, 2, 1, 4],\n            [3, 2, 4, 1],\n            [1, 3, 4, 2],\n            [3, 1, 4, 2],\n            [3, 4, 1, 2],\n            [3, 4, 2, 1],\n            [1, 2, 4, 3],\n            [2, 1, 4, 3],\n            [2, 4, 1, 3],\n            [2, 4, 3, 1],\n            [1, 4, 2, 3],\n            [4, 1, 2, 3],\n            [4, 2, 1, 3],\n            [4, 2, 3, 1],\n            [1, 4, 3, 2],\n            [4, 1, 3, 2],\n            [4, 3, 1, 2],\n            [4, 3, 2, 1],\n        ]\n        self.assertEqual(sorted(permute(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 3]\n        answer2 = [[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]\n        self.assertEqual(sorted(permute(nums2)), sorted(answer2))\n\n    def test_permute_recursive(self):\n        nums1 = [1, 2, 3, 4]\n        answer1 = [\n            [1, 2, 3, 4],\n            [2, 1, 3, 4],\n            [2, 3, 1, 4],\n            [2, 3, 4, 1],\n            [1, 3, 2, 4],\n            [3, 1, 2, 4],\n            [3, 2, 1, 4],\n            [3, 2, 4, 1],\n            [1, 3, 4, 2],\n            [3, 1, 4, 2],\n            [3, 4, 1, 2],\n            [3, 4, 2, 1],\n            [1, 2, 4, 3],\n            [2, 1, 4, 3],\n            [2, 4, 1, 3],\n            [2, 4, 3, 1],\n            [1, 4, 2, 3],\n            [4, 1, 2, 3],\n            [4, 2, 1, 3],\n            [4, 2, 3, 1],\n            [1, 4, 3, 2],\n            [4, 1, 3, 2],\n            [4, 3, 1, 2],\n            [4, 3, 2, 1],\n        ]\n        self.assertEqual(sorted(permute_recursive(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 3]\n        answer2 = [[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]\n        self.assertEqual(sorted(permute_recursive(nums2)), sorted(answer2))\n\n\nclass TestSubsetsUnique(unittest.TestCase):\n    def test_subsets_unique(self):\n        nums1 = [1, 2, 2]\n        answer1 = [(1, 2), (1,), (1, 2, 2), (2,), (), (2, 2)]\n        self.assertEqual(sorted(subsets_unique(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 3, 4]\n        answer2 = [\n            (1, 2),\n            (1, 3),\n            (1, 2, 3, 4),\n            (1,),\n            (2,),\n            (3,),\n            (1, 4),\n            (1, 2, 3),\n            (4,),\n            (),\n            (2, 3),\n            (1, 2, 4),\n            (1, 3, 4),\n            (2, 3, 4),\n            (3, 4),\n            (2, 4),\n        ]\n        self.assertEqual(sorted(subsets_unique(nums2)), sorted(answer2))\n\n\nclass TestSubsets(unittest.TestCase):\n    def test_subsets(self):\n        nums1 = [1, 2, 3]\n        answer1 = [[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]\n        self.assertEqual(sorted(subsets(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 3, 4]\n        answer2 = [\n            [1, 2, 3, 4],\n            [1, 2, 3],\n            [1, 2, 4],\n            [1, 2],\n            [1, 3, 4],\n            [1, 3],\n            [1, 4],\n            [1],\n            [2, 3, 4],\n            [2, 3],\n            [2, 4],\n            [2],\n            [3, 4],\n            [3],\n            [4],\n            [],\n        ]\n        self.assertEqual(sorted(subsets(nums2)), sorted(answer2))\n\n    def test_subsets_v2(self):\n        nums1 = [1, 2, 3]\n        answer1 = [[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]\n        self.assertEqual(sorted(subsets_v2(nums1)), sorted(answer1))\n\n        nums2 = [1, 2, 3, 4]\n        answer2 = [\n            [1, 2, 3, 4],\n            [1, 2, 3],\n            [1, 2, 4],\n            [1, 2],\n            [1, 3, 4],\n            [1, 3],\n            [1, 4],\n            [1],\n            [2, 3, 4],\n            [2, 3],\n            [2, 4],\n            [2],\n            [3, 4],\n            [3],\n            [4],\n            [],\n        ]\n        self.assertEqual(sorted(subsets_v2(nums2)), sorted(answer2))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_bit_manipulation.py",
    "content": "import random\nimport unittest\n\nfrom algorithms.bit_manipulation import (\n    add_bitwise_operator,\n    binary_gap,\n    bytes_big_endian_to_int,\n    bytes_little_endian_to_int,\n    clear_bit,\n    count_flips_to_convert,\n    count_ones_iter,\n    count_ones_recur,\n    find_difference,\n    find_missing_number,\n    find_missing_number2,\n    flip_bit_longest_seq,\n    get_bit,\n    has_alternative_bit,\n    has_alternative_bit_fast,\n    insert_mult_bits,\n    insert_one_bit,\n    int_to_bytes_big_endian,\n    int_to_bytes_little_endian,\n    is_power_of_two,\n    remove_bit,\n    reverse_bits,\n    set_bit,\n    single_number,\n    single_number2,\n    single_number3,\n    subsets,\n    swap_pair,\n    update_bit,\n)\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        \"\"\"Initialize seed.\"\"\"\n        random.seed(\"test\")\n\n    def test_add_bitwise_operator(self):\n        self.assertEqual(5432 + 97823, add_bitwise_operator(5432, 97823))\n        self.assertEqual(0, add_bitwise_operator(0, 0))\n        self.assertEqual(10, add_bitwise_operator(10, 0))\n        self.assertEqual(10, add_bitwise_operator(0, 10))\n\n    def test_count_ones_recur(self):\n\n        # 8 -> 1000\n        self.assertEqual(1, count_ones_recur(8))\n\n        # 109 -> 1101101\n        self.assertEqual(5, count_ones_recur(109))\n\n        # 63 -> 111111\n        self.assertEqual(6, count_ones_recur(63))\n\n        # 0 -> 0\n        self.assertEqual(0, count_ones_recur(0))\n\n    def test_count_ones_iter(self):\n\n        # 8 -> 1000\n        self.assertEqual(1, count_ones_iter(8))\n\n        # 109 -> 1101101\n        self.assertEqual(5, count_ones_iter(109))\n\n        # 63 -> 111111\n        self.assertEqual(6, count_ones_iter(63))\n\n        # 0 -> 0\n        self.assertEqual(0, count_ones_iter(0))\n\n    def test_count_flips_to_convert(self):\n        # 29: 11101 and 15: 01111\n        self.assertEqual(2, count_flips_to_convert(29, 15))\n        # 45: 0000101101 and 987: 1111011011\n        self.assertEqual(8, count_flips_to_convert(45, 987))\n        # 34: 100010\n        self.assertEqual(0, count_flips_to_convert(34, 34))\n        # 34: 100010 and 53: 110101\n        self.assertEqual(4, count_flips_to_convert(34, 53))\n\n    def test_find_missing_number(self):\n\n        self.assertEqual(7, find_missing_number([4, 1, 3, 0, 6, 5, 2]))\n        self.assertEqual(0, find_missing_number([1]))\n        self.assertEqual(1, find_missing_number([0]))\n\n        nums = [i for i in range(100000) if i != 12345]\n        random.shuffle(nums)\n        self.assertEqual(12345, find_missing_number(nums))\n\n    def test_find_missing_number2(self):\n\n        self.assertEqual(7, find_missing_number2([4, 1, 3, 0, 6, 5, 2]))\n        self.assertEqual(0, find_missing_number2([1]))\n        self.assertEqual(1, find_missing_number2([0]))\n\n        nums = [i for i in range(100000) if i != 12345]\n        random.shuffle(nums)\n        self.assertEqual(12345, find_missing_number2(nums))\n\n    def test_flip_bit_longest_seq(self):\n        # 1775: 11011101111\n        self.assertEqual(8, flip_bit_longest_seq(1775))\n        # 5: 101\n        self.assertEqual(3, flip_bit_longest_seq(5))\n        # 71: 1000111\n        self.assertEqual(4, flip_bit_longest_seq(71))\n        # 0: 0\n        self.assertEqual(1, flip_bit_longest_seq(0))\n\n    def test_is_power_of_two(self):\n\n        self.assertTrue(is_power_of_two(64))\n        self.assertFalse(is_power_of_two(91))\n        self.assertTrue(is_power_of_two(2**1001))\n        self.assertTrue(is_power_of_two(1))\n        self.assertFalse(is_power_of_two(0))\n\n    def test_reverse_bits(self):\n\n        self.assertEqual(43261596, reverse_bits(964176192))\n        self.assertEqual(964176192, reverse_bits(43261596))\n        self.assertEqual(1, reverse_bits(2147483648))\n\n        # bin(0) => 00000000000000000000000000000000\n        self.assertEqual(0, reverse_bits(0))\n\n        # bin(2**32 - 1) => 11111111111111111111111111111111\n        self.assertEqual(2**32 - 1, reverse_bits(2**32 - 1))\n\n    def test_single_number(self):\n\n        random.seed(\"test\")\n\n        self.assertEqual(0, single_number([1, 0, 2, 1, 2, 3, 3]))\n        self.assertEqual(101, single_number([101]))\n\n        single = random.randint(1, 100000)\n        nums = [random.randint(1, 100000) for _ in range(1000)]\n        nums *= 2  # nums contains pairs of random integers\n        nums.append(single)\n        random.shuffle(nums)\n\n        self.assertEqual(single, single_number(nums))\n\n    def test_single_number2(self):\n\n        self.assertEqual(3, single_number2([4, 2, 3, 2, 1, 1, 4, 2, 4, 1]))\n        single = random.randint(1, 100000)\n        nums = [random.randint(1, 100000) for _ in range(1000)]\n        nums *= 3  # nums contains triplets of random integers\n        nums.append(single)\n        random.shuffle(nums)\n        self.assertEqual(single, single_number2(nums))\n\n    def test_single_number3(self):\n        self.assertEqual(sorted([2, 5]), sorted(single_number3([2, 1, 5, 6, 6, 1])))\n        self.assertEqual(sorted([4, 3]), sorted(single_number3([9, 9, 4, 3])))\n\n    def test_subsets(self):\n\n        self.assertSetEqual(\n            subsets([1, 2, 3]),\n            {(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)},\n        )\n\n        self.assertSetEqual(\n            subsets([10, 20, 30, 40]),\n            {\n                (10, 40),\n                (10, 20, 40),\n                (10, 30),\n                (10, 20, 30, 40),\n                (40,),\n                (10, 30, 40),\n                (30,),\n                (20, 30),\n                (30, 40),\n                (10,),\n                (),\n                (10, 20),\n                (20, 40),\n                (20, 30, 40),\n                (10, 20, 30),\n                (20,),\n            },\n        )\n\n    def test_get_bit(self):\n        # 22 = 10110\n        self.assertEqual(1, get_bit(22, 2))\n        self.assertEqual(0, get_bit(22, 3))\n\n    def test_set_bit(self):\n        # 22 = 10110  --> after set bit at 3th position: 30 = 11110\n        self.assertEqual(30, set_bit(22, 3))\n\n    def test_clear_bit(self):\n        # 22 = 10110 --> after clear bit at 2nd position: 20 = 10010\n        self.assertEqual(18, clear_bit(22, 2))\n\n    def test_update_bit(self):\n        # 22 = 10110 --> after update bit at 3th position with\n        # value 1: 30 = 11110\n        self.assertEqual(30, update_bit(22, 3, 1))\n        # 22 = 10110 --> after update bit at 2nd position with\n        # value 0: 20 = 10010\n        self.assertEqual(18, update_bit(22, 2, 0))\n\n    def test_int_to_bytes_big_endian(self):\n        self.assertEqual(b\"\\x11\", int_to_bytes_big_endian(17))\n\n    def test_int_to_bytes_little_endian(self):\n        self.assertEqual(b\"\\x11\", int_to_bytes_little_endian(17))\n\n    def test_bytes_big_endian_to_int(self):\n        self.assertEqual(17, bytes_big_endian_to_int(b\"\\x11\"))\n\n    def test_bytes_little_endian_to_int(self):\n        self.assertEqual(17, bytes_little_endian_to_int(b\"\\x11\"))\n\n    def test_swap_pair(self):\n        # 22: 10110  --> 41: 101001\n        self.assertEqual(41, swap_pair(22))\n        # 10: 1010   --> 5 : 0101\n        self.assertEqual(5, swap_pair(10))\n\n    def test_find_difference(self):\n        self.assertEqual(\"e\", find_difference(\"abcd\", \"abecd\"))\n\n    def test_has_alternative_bit(self):\n        self.assertTrue(has_alternative_bit(5))\n        self.assertFalse(has_alternative_bit(7))\n        self.assertFalse(has_alternative_bit(11))\n        self.assertTrue(has_alternative_bit(10))\n\n    def test_has_alternative_bit_fast(self):\n        self.assertTrue(has_alternative_bit_fast(5))\n        self.assertFalse(has_alternative_bit_fast(7))\n        self.assertFalse(has_alternative_bit_fast(11))\n        self.assertTrue(has_alternative_bit_fast(10))\n\n    def test_insert_one_bit(self):\n        \"\"\"\n        Input: num = 10101 (21)\n        insert_one_bit(num, 1, 2): 101101 (45)\n        insert_one_bit(num, 0 ,2): 101001 (41)\n        insert_one_bit(num, 1, 5): 110101 (53)\n        insert_one_bit(num, 1, 0): 101010 (42)\n        \"\"\"\n        self.assertEqual(45, insert_one_bit(21, 1, 2))\n        self.assertEqual(41, insert_one_bit(21, 0, 2))\n        self.assertEqual(53, insert_one_bit(21, 1, 5))\n        self.assertEqual(43, insert_one_bit(21, 1, 0))\n\n    def test_insert_mult_bits(self):\n        \"\"\"\n        Input: num = 101 (5)\n        insert_mult_bits(num, 7, 3, 1): 101111 (47)\n        insert_mult_bits(num, 7, 3, 0): 101111 (47)\n        insert_mult_bits(num, 7, 3, 3): 111101 (61)\n        \"\"\"\n        self.assertEqual(47, insert_mult_bits(5, 7, 3, 1))\n        self.assertEqual(47, insert_mult_bits(5, 7, 3, 0))\n        self.assertEqual(61, insert_mult_bits(5, 7, 3, 3))\n\n    def test_remove_bit(self):\n        \"\"\"\n        Input: num = 10101 (21)\n        remove_bit(num, 2): output = 1001 (9)\n        remove_bit(num, 4): output = 101 (5)\n        remove_bit(num, 0): output = 1010 (10)\n        \"\"\"\n        self.assertEqual(9, remove_bit(21, 2))\n        self.assertEqual(5, remove_bit(21, 4))\n        self.assertEqual(10, remove_bit(21, 0))\n\n    def test_binary_gap(self):\n        # 22 = 10110\n        self.assertEqual(2, binary_gap(22))\n        # 6 = 110\n        self.assertEqual(1, binary_gap(6))\n        # 8 = 1000\n        self.assertEqual(0, binary_gap(8))\n        # 145 = 10010001\n        self.assertEqual(4, binary_gap(145))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_community_algorithms.py",
    "content": "\"\"\"Tests for community-contributed algorithms.\n\nCovers algorithms adopted from open PRs and implemented to match\nthe repo's code standards.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport unittest\n\nfrom algorithms.backtracking.minimax import minimax\nfrom algorithms.bit_manipulation.gray_code import gray_code, gray_to_binary\nfrom algorithms.data_structures.kd_tree import KDTree\nfrom algorithms.dynamic_programming.bitmask import tsp\nfrom algorithms.dynamic_programming.count_paths_dp import (\n    count_paths_dp,\n    count_paths_memo,\n    count_paths_recursive,\n)\nfrom algorithms.graph.blossom import max_matching\nfrom algorithms.math.linear_regression import linear_regression, r_squared, rmse\nfrom algorithms.math.manhattan_distance import manhattan_distance\nfrom algorithms.math.polynomial_division import polynomial_division\nfrom algorithms.searching.exponential_search import exponential_search\nfrom algorithms.searching.sentinel_search import sentinel_search\nfrom algorithms.string.alphabet_board_path import alphabet_board_path\nfrom algorithms.string.manacher import manacher\nfrom algorithms.string.swap_characters import can_swap_to_equal\nfrom algorithms.string.z_algorithm import compute_z_array, z_search\n\n\nclass TestManacher(unittest.TestCase):\n    def test_odd_palindrome(self):\n        result = manacher(\"babad\")\n        self.assertIn(result, (\"bab\", \"aba\"))\n\n    def test_even_palindrome(self):\n        self.assertEqual(manacher(\"cbbd\"), \"bb\")\n\n    def test_single_char(self):\n        self.assertEqual(manacher(\"a\"), \"a\")\n\n    def test_full_palindrome(self):\n        self.assertEqual(manacher(\"racecar\"), \"racecar\")\n\n    def test_empty(self):\n        self.assertEqual(manacher(\"\"), \"\")\n\n    def test_all_same(self):\n        self.assertEqual(manacher(\"aaaa\"), \"aaaa\")\n\n\nclass TestZAlgorithm(unittest.TestCase):\n    def test_z_array_basic(self):\n        z = compute_z_array(\"aabxaa\")\n        self.assertEqual(z[0], 6)\n        self.assertEqual(z[1], 1)\n        self.assertEqual(z[4], 2)\n\n    def test_z_search_found(self):\n        self.assertEqual(z_search(\"abxabcabcaby\", \"abcaby\"), [6])\n\n    def test_z_search_multiple(self):\n        self.assertEqual(z_search(\"aaaa\", \"aa\"), [0, 1, 2])\n\n    def test_z_search_not_found(self):\n        self.assertEqual(z_search(\"hello\", \"xyz\"), [])\n\n    def test_z_search_empty(self):\n        self.assertEqual(z_search(\"abc\", \"\"), [])\n        self.assertEqual(z_search(\"\", \"abc\"), [])\n\n    def test_z_array_empty(self):\n        self.assertEqual(compute_z_array(\"\"), [])\n\n\nclass TestGrayCode(unittest.TestCase):\n    def test_2bit(self):\n        self.assertEqual(gray_code(2), [0, 1, 3, 2])\n\n    def test_3bit(self):\n        self.assertEqual(gray_code(3), [0, 1, 3, 2, 6, 7, 5, 4])\n\n    def test_1bit(self):\n        self.assertEqual(gray_code(1), [0, 1])\n\n    def test_0bit(self):\n        self.assertEqual(gray_code(0), [0])\n\n    def test_successive_differ_by_one_bit(self):\n        codes = gray_code(4)\n        for i in range(len(codes) - 1):\n            xor = codes[i] ^ codes[i + 1]\n            self.assertTrue(xor & (xor - 1) == 0)  # power of 2\n\n    def test_gray_to_binary_roundtrip(self):\n        for n in range(16):\n            gray = n ^ (n >> 1)\n            self.assertEqual(gray_to_binary(gray), n)\n\n\nclass TestKdTree(unittest.TestCase):\n    def test_nearest_basic(self):\n        points = [(2, 3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)]\n        tree = KDTree(points)\n        self.assertEqual(tree.nearest((9, 2)), (8, 1))\n\n    def test_nearest_exact(self):\n        points = [(1, 1), (2, 2), (3, 3)]\n        tree = KDTree(points)\n        self.assertEqual(tree.nearest((2, 2)), (2, 2))\n\n    def test_nearest_3d(self):\n        points = [(0, 0, 0), (1, 1, 1), (2, 2, 2)]\n        tree = KDTree(points)\n        self.assertEqual(tree.nearest((1, 1, 0)), (1, 1, 1))\n\n    def test_single_point(self):\n        tree = KDTree([(5, 5)])\n        self.assertEqual(tree.nearest((0, 0)), (5, 5))\n\n\nclass TestExponentialSearch(unittest.TestCase):\n    def test_found(self):\n        arr = [1, 3, 5, 7, 9, 11, 13, 15]\n        self.assertEqual(exponential_search(arr, 7), 3)\n\n    def test_first_element(self):\n        self.assertEqual(exponential_search([1, 2, 3], 1), 0)\n\n    def test_last_element(self):\n        self.assertEqual(exponential_search([1, 2, 3], 3), 2)\n\n    def test_not_found(self):\n        self.assertEqual(exponential_search([1, 2, 3], 4), -1)\n\n    def test_empty(self):\n        self.assertEqual(exponential_search([], 1), -1)\n\n    def test_single_element_found(self):\n        self.assertEqual(exponential_search([42], 42), 0)\n\n    def test_single_element_not_found(self):\n        self.assertEqual(exponential_search([42], 99), -1)\n\n\nclass TestSentinelSearch(unittest.TestCase):\n    def test_found(self):\n        arr = [4, 2, 7, 1, 9]\n        self.assertEqual(sentinel_search(arr, 7), 2)\n\n    def test_last_element(self):\n        arr = [4, 2, 7, 1, 9]\n        self.assertEqual(sentinel_search(arr, 9), 4)\n\n    def test_not_found(self):\n        arr = [4, 2, 7, 1, 9]\n        self.assertEqual(sentinel_search(arr, 5), -1)\n\n    def test_empty(self):\n        self.assertEqual(sentinel_search([], 1), -1)\n\n    def test_restores_array(self):\n        arr = [1, 2, 3]\n        sentinel_search(arr, 99)\n        self.assertEqual(arr, [1, 2, 3])\n\n\nclass TestManhattanDistance(unittest.TestCase):\n    def test_2d(self):\n        self.assertEqual(manhattan_distance((1, 2), (4, 6)), 7)\n\n    def test_3d(self):\n        self.assertEqual(manhattan_distance((0, 0, 0), (1, 2, 3)), 6)\n\n    def test_same_point(self):\n        self.assertEqual(manhattan_distance((3, 4), (3, 4)), 0)\n\n    def test_negative(self):\n        self.assertEqual(manhattan_distance((-1, -1), (1, 1)), 4)\n\n\nclass TestLinearRegression(unittest.TestCase):\n    def test_basic_fit(self):\n        x = [1, 2, 3, 4, 5]\n        y = [2, 4, 5, 4, 5]\n        m, b = linear_regression(x, y)\n        self.assertAlmostEqual(m, 0.6)\n        self.assertAlmostEqual(b, 2.2)\n\n    def test_perfect_line(self):\n        x = [0, 1, 2, 3]\n        y = [0, 2, 4, 6]\n        m, b = linear_regression(x, y)\n        self.assertAlmostEqual(m, 2.0)\n        self.assertAlmostEqual(b, 0.0)\n\n    def test_r_squared_perfect(self):\n        x = [0, 1, 2, 3]\n        y = [0, 2, 4, 6]\n        self.assertAlmostEqual(r_squared(x, y), 1.0)\n\n    def test_rmse_perfect(self):\n        x = [0, 1, 2, 3]\n        y = [0, 2, 4, 6]\n        self.assertAlmostEqual(rmse(x, y), 0.0)\n\n    def test_rmse_nonperfect(self):\n        x = [1, 2, 3, 4, 5]\n        y = [2, 4, 5, 4, 5]\n        self.assertGreater(rmse(x, y), 0)\n\n    def test_too_few_points(self):\n        with self.assertRaises(ValueError):\n            linear_regression([1], [2])\n\n\nclass TestPolynomialDivision(unittest.TestCase):\n    def test_basic(self):\n        # (x^2 - 3x + 2) / (x - 1) = (x - 2), remainder 0\n        q, r = polynomial_division([1, -3, 2], [1, -1])\n        self.assertEqual(q, [1.0, -2.0])\n        self.assertEqual(r, [0.0])\n\n    def test_with_remainder(self):\n        # (x^2 + 1) / (x - 1) = (x + 1), remainder 2\n        q, r = polynomial_division([1, 0, 1], [1, -1])\n        self.assertAlmostEqual(q[0], 1.0)\n        self.assertAlmostEqual(q[1], 1.0)\n        self.assertAlmostEqual(r[0], 2.0)\n\n    def test_divide_by_zero(self):\n        with self.assertRaises(ZeroDivisionError):\n            polynomial_division([1, 2], [0, 0])\n\n\nclass TestAlphabetBoardPath(unittest.TestCase):\n    def test_leet(self):\n        path = alphabet_board_path(\"leet\")\n        self.assertIn(\"!\", path)\n        # Verify length: must spell 4 chars so exactly 4 '!'s\n        self.assertEqual(path.count(\"!\"), 4)\n\n    def test_code(self):\n        path = alphabet_board_path(\"code\")\n        self.assertEqual(path.count(\"!\"), 4)\n\n    def test_z(self):\n        # 'z' is at row 5, col 0 — must go down 5 from 'a'\n        path = alphabet_board_path(\"z\")\n        self.assertEqual(path, \"DDDDD!\")\n\n    def test_za(self):\n        path = alphabet_board_path(\"za\")\n        self.assertEqual(path, \"DDDDD!UUUUU!\")\n\n\nclass TestSwapCharacters(unittest.TestCase):\n    def test_can_swap(self):\n        self.assertTrue(can_swap_to_equal(\"ab\", \"ba\"))\n\n    def test_identical(self):\n        # No differences — need exactly 2, so False\n        self.assertFalse(can_swap_to_equal(\"ab\", \"ab\"))\n\n    def test_too_many_diffs(self):\n        self.assertFalse(can_swap_to_equal(\"abc\", \"xyz\"))\n\n    def test_different_lengths(self):\n        self.assertFalse(can_swap_to_equal(\"ab\", \"abc\"))\n\n    def test_one_diff(self):\n        self.assertFalse(can_swap_to_equal(\"ab\", \"ac\"))\n\n\nclass TestMinimax(unittest.TestCase):\n    def test_depth_2(self):\n        # max(min(3,5), min(2,9)) = max(3,2) = 3\n        self.assertEqual(minimax(2, True, [3, 5, 2, 9]), 3)\n\n    def test_depth_3(self):\n        self.assertEqual(minimax(3, True, [3, 5, 2, 9, 12, 5, 23, 23]), 12)\n\n    def test_single_leaf(self):\n        self.assertEqual(minimax(0, True, [42]), 42)\n\n    def test_minimizing(self):\n        self.assertEqual(minimax(1, False, [3, 5]), 3)\n\n\nclass TestTsp(unittest.TestCase):\n    def test_4_cities(self):\n        dist = [\n            [0, 10, 15, 20],\n            [10, 0, 35, 25],\n            [15, 35, 0, 30],\n            [20, 25, 30, 0],\n        ]\n        self.assertEqual(tsp(dist), 80)\n\n    def test_3_cities(self):\n        dist = [\n            [0, 1, 2],\n            [1, 0, 3],\n            [2, 3, 0],\n        ]\n        self.assertEqual(tsp(dist), 6)\n\n\nclass TestCountPathsDp(unittest.TestCase):\n    def test_3x7(self):\n        self.assertEqual(count_paths_dp(3, 7), 28)\n        self.assertEqual(count_paths_memo(3, 7), 28)\n        self.assertEqual(count_paths_recursive(3, 7), 28)\n\n    def test_3x3(self):\n        self.assertEqual(count_paths_dp(3, 3), 6)\n\n    def test_1x1(self):\n        self.assertEqual(count_paths_dp(1, 1), 1)\n\n    def test_2x2(self):\n        self.assertEqual(count_paths_dp(2, 2), 2)\n\n\nclass TestBlossom(unittest.TestCase):\n    def test_path_graph(self):\n        # Path: 0-1-2-3 → max matching has 2 edges\n        matching = max_matching(4, [(0, 1), (1, 2), (2, 3)])\n        self.assertEqual(len(matching), 2)\n\n    def test_triangle(self):\n        # Triangle: 0-1-2 → max matching has 1 edge\n        matching = max_matching(3, [(0, 1), (1, 2), (0, 2)])\n        self.assertEqual(len(matching), 1)\n\n    def test_empty_graph(self):\n        matching = max_matching(3, [])\n        self.assertEqual(len(matching), 0)\n\n    def test_complete_4(self):\n        # K4 → max matching has 2 edges\n        edges = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]\n        matching = max_matching(4, edges)\n        self.assertEqual(len(matching), 2)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_compression.py",
    "content": "import unittest\n\nfrom algorithms.compression.elias import elias_delta, elias_gamma\nfrom algorithms.compression.huffman_coding import HuffmanCoding\nfrom algorithms.compression.rle_compression import decode_rle, encode_rle\n\n\nclass TestHuffmanCoding(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        cls.file_in_name = \"huffman_coding_in.txt\"\n        cls.file_out_bin_name = \"huffman_coding_out.bin\"\n        cls.file_out_name = \"huffman_coding_out.txt\"\n\n    def setUp(self):\n        import random\n\n        random.seed(1951)\n        with open(self.file_in_name, \"wb\") as file_in:\n            for _ in range(10000):\n                file_in.write(bytes([random.randrange(0, 256)]))\n\n    def test_huffman_coding(self):\n        HuffmanCoding.encode_file(self.file_in_name, self.file_out_bin_name)\n        HuffmanCoding.decode_file(self.file_out_bin_name, self.file_out_name)\n\n        with (\n            open(self.file_in_name, \"rb\") as file_1,\n            open(self.file_out_name, \"rb\") as file_2,\n        ):\n            content_1 = file_1.read()\n            content_2 = file_2.read()\n\n            self.assertEqual(content_1, content_2)\n\n    def tearDown(self):\n        import os\n\n        os.remove(self.file_in_name)\n        os.remove(self.file_out_bin_name)\n        os.remove(self.file_out_name)\n\n\nclass TestRLECompression(unittest.TestCase):\n    def test_encode_rle(self):\n        self.assertEqual(\n            \"12W1B12W3B24W1B14W\",\n            encode_rle(\n                \"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW\"\n            ),\n        )\n\n    def test_decode_rle(self):\n        self.assertEqual(\n            \"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW\",\n            decode_rle(\"12W1B12W3B24W1B14W\"),\n        )\n\n\nclass TestEliasCoding(unittest.TestCase):\n    def test_elias_gamma(self):\n        correct_result = [\n            \"0\",\n            \"00\",\n            \"100\",\n            \"101\",\n            \"11000\",\n            \"11001\",\n            \"11010\",\n            \"11011\",\n            \"1110000\",\n            \"1110001\",\n            \"1110010\",\n        ]\n\n        result = []\n        for i in range(11):\n            result.append(elias_gamma(i))\n\n        self.assertEqual(correct_result, result)\n\n    def test_elias_delta(self):\n        correct_result = [\n            \"0\",\n            \"000\",\n            \"1000\",\n            \"1001\",\n            \"10100\",\n            \"10101\",\n            \"10110\",\n            \"10111\",\n            \"11000000\",\n            \"11000001\",\n            \"11000010\",\n        ]\n\n        result = []\n        for i in range(11):\n            result.append(elias_delta(i))\n\n        self.assertEqual(correct_result, result)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_data_structures.py",
    "content": "\"\"\"Tests for data structures in algorithms/data_structures.\"\"\"\n\nimport unittest\n\nfrom algorithms.data_structures.avl_tree import AvlTree\nfrom algorithms.data_structures.hash_table import HashTable, ResizableHashTable\nfrom algorithms.data_structures.red_black_tree import RBNode, RBTree\nfrom algorithms.data_structures.segment_tree import SegmentTree\nfrom algorithms.data_structures.separate_chaining_hash_table import (\n    SeparateChainingHashTable,\n)\nfrom algorithms.data_structures.trie import Trie\nfrom algorithms.data_structures.union_find import Union\n\n\nclass TestRBTree(unittest.TestCase):\n    def _make_tree(self, values):\n        tree = RBTree()\n        for v in values:\n            tree.insert(RBNode(v, 1))\n        return tree\n\n    def test_insert_single(self):\n        tree = self._make_tree([5])\n        result = tree.inorder()\n        self.assertEqual(len(result), 1)\n        self.assertEqual(result[0][\"val\"], 5)\n\n    def test_insert_multiple_sorted(self):\n        values = [11, 2, 14, 1, 7, 15, 5, 8, 4]\n        tree = self._make_tree(values)\n        result = tree.inorder()\n        vals = [r[\"val\"] for r in result]\n        self.assertEqual(vals, sorted(values))\n\n    def test_root_is_black(self):\n        tree = self._make_tree([10, 5, 15])\n        self.assertEqual(tree.root.color, 0)\n\n    def test_empty_tree(self):\n        tree = RBTree()\n        self.assertIsNone(tree.root)\n        self.assertEqual(tree.inorder(), [])\n\n    def test_insert_duplicates_order(self):\n        tree = self._make_tree([3, 1, 2])\n        result = tree.inorder()\n        vals = [r[\"val\"] for r in result]\n        self.assertEqual(vals, [1, 2, 3])\n\n\nclass TestAvlTree(unittest.TestCase):\n    def test_insert_single(self):\n        tree = AvlTree()\n        tree.insert(10)\n        self.assertIsNotNone(tree.node)\n        self.assertEqual(tree.node.val, 10)\n\n    def test_insert_multiple_root_exists(self):\n        tree = AvlTree()\n        for v in [5, 3, 7, 1, 4]:\n            tree.insert(v)\n        self.assertIsNotNone(tree.node)\n\n    def test_balanced_after_insert(self):\n        tree = AvlTree()\n        for v in [1, 2, 3, 4, 5]:\n            tree.insert(v)\n        # Tree should remain balanced; height should be <= log2(5)+1 ~ 3\n        self.assertLessEqual(tree.height, 3)\n\n    def test_empty_tree(self):\n        tree = AvlTree()\n        self.assertIsNone(tree.node)\n        self.assertEqual(tree.in_order_traverse(), [])\n\n    def test_in_order_traverse_populated(self):\n        tree = AvlTree()\n        for v in [5, 3, 7, 1, 4]:\n            tree.insert(v)\n        self.assertEqual(tree.in_order_traverse(), [1, 3, 4, 5, 7])\n\n    def test_insert_balance_factor(self):\n        tree = AvlTree()\n        for v in [5, 4, 3, 2, 1]:\n            tree.insert(v)\n        # After balancing, the balance factor should be within [-1, 1]\n        self.assertIn(tree.balance, [-1, 0, 1])\n\n\nclass TestTrie(unittest.TestCase):\n    def test_insert_and_search(self):\n        trie = Trie()\n        trie.insert(\"apple\")\n        self.assertTrue(trie.search(\"apple\"))\n\n    def test_search_missing(self):\n        trie = Trie()\n        trie.insert(\"apple\")\n        self.assertFalse(trie.search(\"app\"))\n\n    def test_starts_with(self):\n        trie = Trie()\n        trie.insert(\"apple\")\n        self.assertTrue(trie.starts_with(\"app\"))\n        self.assertFalse(trie.starts_with(\"apl\"))\n\n    def test_empty_trie(self):\n        trie = Trie()\n        self.assertFalse(trie.search(\"anything\"))\n        self.assertFalse(trie.starts_with(\"a\"))\n\n    def test_multiple_words(self):\n        trie = Trie()\n        for w in [\"cat\", \"car\", \"card\", \"care\"]:\n            trie.insert(w)\n        self.assertTrue(trie.search(\"card\"))\n        self.assertFalse(trie.search(\"ca\"))\n        self.assertTrue(trie.starts_with(\"ca\"))\n\n    def test_insert_single_char(self):\n        trie = Trie()\n        trie.insert(\"a\")\n        self.assertTrue(trie.search(\"a\"))\n        self.assertFalse(trie.search(\"b\"))\n\n\nclass TestUnionFind(unittest.TestCase):\n    def test_add_and_root(self):\n        uf = Union()\n        uf.add(1)\n        self.assertEqual(uf.root(1), 1)\n\n    def test_unite_connects(self):\n        uf = Union()\n        uf.add(1)\n        uf.add(2)\n        uf.unite(1, 2)\n        self.assertEqual(uf.root(1), uf.root(2))\n\n    def test_not_connected(self):\n        uf = Union()\n        uf.add(1)\n        uf.add(2)\n        self.assertNotEqual(uf.root(1), uf.root(2))\n\n    def test_count_decrements_on_unite(self):\n        uf = Union()\n        uf.add(1)\n        uf.add(2)\n        uf.add(3)\n        self.assertEqual(uf.count, 3)\n        uf.unite(1, 2)\n        self.assertEqual(uf.count, 2)\n\n    def test_unite_same_element(self):\n        uf = Union()\n        uf.add(1)\n        uf.unite(1, 1)\n        self.assertEqual(uf.count, 1)\n\n    def test_transitive_connectivity(self):\n        uf = Union()\n        for x in [1, 2, 3]:\n            uf.add(x)\n        uf.unite(1, 2)\n        uf.unite(2, 3)\n        self.assertEqual(uf.root(1), uf.root(3))\n\n\nclass TestSegmentTree(unittest.TestCase):\n    def test_max_query(self):\n        tree = SegmentTree([2, 4, 5, 3, 4], max)\n        self.assertEqual(tree.query(2, 4), 5)\n\n    def test_sum_query(self):\n        tree = SegmentTree([1, 2, 3, 4, 5], lambda a, b: a + b)\n        self.assertEqual(tree.query(0, 4), 15)\n\n    def test_single_element_query(self):\n        tree = SegmentTree([7, 2, 9], max)\n        self.assertEqual(tree.query(0, 0), 7)\n        self.assertEqual(tree.query(2, 2), 9)\n\n    def test_full_range_max(self):\n        arr = [3, 1, 4, 1, 5, 9, 2, 6]\n        tree = SegmentTree(arr, max)\n        self.assertEqual(tree.query(0, len(arr) - 1), 9)\n\n\nclass TestHashTable(unittest.TestCase):\n    def test_put_and_get(self):\n        ht = HashTable()\n        ht.put(1, \"one\")\n        self.assertEqual(ht.get(1), \"one\")\n\n    def test_get_missing(self):\n        ht = HashTable()\n        self.assertIsNone(ht.get(99))\n\n    def test_delete(self):\n        ht = HashTable()\n        ht.put(1, \"one\")\n        ht.del_(1)\n        self.assertIsNone(ht.get(1))\n\n    def test_update_existing(self):\n        ht = HashTable()\n        ht.put(1, \"one\")\n        ht.put(1, \"ONE\")\n        self.assertEqual(ht.get(1), \"ONE\")\n\n    def test_len(self):\n        ht = HashTable()\n        ht.put(1, \"a\")\n        ht.put(2, \"b\")\n        self.assertEqual(len(ht), 2)\n\n    def test_bracket_operators(self):\n        ht = HashTable()\n        ht[5] = \"five\"\n        self.assertEqual(ht[5], \"five\")\n        del ht[5]\n        self.assertIsNone(ht[5])\n\n\nclass TestResizableHashTable(unittest.TestCase):\n    def test_put_and_get(self):\n        ht = ResizableHashTable()\n        ht.put(1, \"a\")\n        self.assertEqual(ht.get(1), \"a\")\n\n    def test_resizes_automatically(self):\n        ht = ResizableHashTable()\n        for i in range(20):\n            ht.put(i, str(i))\n        for i in range(20):\n            self.assertEqual(ht.get(i), str(i))\n\n\nclass TestSeparateChainingHashTable(unittest.TestCase):\n    def test_put_and_get(self):\n        table = SeparateChainingHashTable()\n        table.put(\"hello\", \"world\")\n        self.assertEqual(table.get(\"hello\"), \"world\")\n\n    def test_get_missing(self):\n        table = SeparateChainingHashTable()\n        self.assertIsNone(table.get(\"missing\"))\n\n    def test_delete(self):\n        table = SeparateChainingHashTable()\n        table.put(\"key\", \"value\")\n        table.del_(\"key\")\n        self.assertIsNone(table.get(\"key\"))\n\n    def test_len(self):\n        table = SeparateChainingHashTable()\n        table.put(\"a\", 1)\n        table.put(\"b\", 2)\n        self.assertEqual(len(table), 2)\n\n    def test_collision_handling(self):\n        # Force collision by using small table\n        table = SeparateChainingHashTable(size=1)\n        table.put(\"x\", 10)\n        table.put(\"y\", 20)\n        self.assertEqual(table.get(\"x\"), 10)\n        self.assertEqual(table.get(\"y\"), 20)\n\n    def test_bracket_operators(self):\n        table = SeparateChainingHashTable()\n        table[\"k\"] = \"v\"\n        self.assertEqual(table[\"k\"], \"v\")\n        del table[\"k\"]\n        self.assertIsNone(table[\"k\"])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_dynamic_programming.py",
    "content": "import unittest\n\nfrom algorithms.dynamic_programming import (\n    Item,\n    Job,\n    climb_stairs,\n    climb_stairs_optimized,\n    combination_sum_bottom_up,\n    combination_sum_topdown,\n    count,\n    edit_distance,\n    egg_drop,\n    fib_iter,\n    fib_list,\n    fib_recursive,\n    find_k_factor,\n    get_maximum_value,\n    hosoya_testing,\n    house_robber,\n    int_divide,\n    longest_increasing_subsequence,\n    max_profit_naive,\n    max_profit_optimized,\n    planting_trees,\n    regex_matching,\n    schedule,\n)\n\n\nclass TestBuySellStock(unittest.TestCase):\n    def test_max_profit_naive(self):\n        self.assertEqual(max_profit_naive([7, 1, 5, 3, 6, 4]), 5)\n        self.assertEqual(max_profit_naive([7, 6, 4, 3, 1]), 0)\n\n    def test_max_profit_optimized(self):\n        self.assertEqual(max_profit_optimized([7, 1, 5, 3, 6, 4]), 5)\n        self.assertEqual(max_profit_optimized([7, 6, 4, 3, 1]), 0)\n\n\nclass TestClimbingStairs(unittest.TestCase):\n    def test_climb_stairs(self):\n        self.assertEqual(climb_stairs(2), 2)\n        self.assertEqual(climb_stairs(10), 89)\n\n    def test_climb_stairs_optimized(self):\n        self.assertEqual(climb_stairs_optimized(2), 2)\n        self.assertEqual(climb_stairs_optimized(10), 89)\n\n\nclass TestCoinChange(unittest.TestCase):\n    def test_count(self):\n        self.assertEqual(count([1, 2, 3], 4), 4)\n        self.assertEqual(count([2, 5, 3, 6], 10), 5)\n\n\nclass TestCombinationSum(unittest.TestCase):\n    def test_combination_sum_topdown(self):\n        self.assertEqual(combination_sum_topdown([1, 2, 3], 4), 7)\n\n    def test_combination_sum_bottom_up(self):\n        self.assertEqual(combination_sum_bottom_up([1, 2, 3], 4), 7)\n\n\nclass TestEditDistance(unittest.TestCase):\n    def test_edit_distance(self):\n        self.assertEqual(edit_distance(\"food\", \"money\"), 4)\n        self.assertEqual(edit_distance(\"horse\", \"ros\"), 3)\n\n\nclass TestEggDrop(unittest.TestCase):\n    def test_egg_drop(self):\n        self.assertEqual(egg_drop(1, 2), 2)\n        self.assertEqual(egg_drop(2, 6), 3)\n        self.assertEqual(egg_drop(3, 14), 4)\n\n\nclass TestFib(unittest.TestCase):\n    def test_fib_recursive(self):\n        self.assertEqual(fib_recursive(10), 55)\n        self.assertEqual(fib_recursive(30), 832040)\n\n    def test_fib_list(self):\n        self.assertEqual(fib_list(10), 55)\n        self.assertEqual(fib_list(30), 832040)\n\n    def test_fib_iter(self):\n        self.assertEqual(fib_iter(10), 55)\n        self.assertEqual(fib_iter(30), 832040)\n\n\nclass TestHosoyaTriangle(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file hosoya_triangle\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_hosoya(self):\n        self.assertEqual([1], hosoya_testing(1))\n        self.assertEqual(\n            [1, 1, 1, 2, 1, 2, 3, 2, 2, 3, 5, 3, 4, 3, 5, 8, 5, 6, 6, 5, 8],\n            hosoya_testing(6),\n        )\n        self.assertEqual(\n            [\n                1,\n                1,\n                1,\n                2,\n                1,\n                2,\n                3,\n                2,\n                2,\n                3,\n                5,\n                3,\n                4,\n                3,\n                5,\n                8,\n                5,\n                6,\n                6,\n                5,\n                8,\n                13,\n                8,\n                10,\n                9,\n                10,\n                8,\n                13,\n                21,\n                13,\n                16,\n                15,\n                15,\n                16,\n                13,\n                21,\n                34,\n                21,\n                26,\n                24,\n                25,\n                24,\n                26,\n                21,\n                34,\n                55,\n                34,\n                42,\n                39,\n                40,\n                40,\n                39,\n                42,\n                34,\n                55,\n            ],\n            hosoya_testing(10),\n        )\n\n\nclass TestHouseRobber(unittest.TestCase):\n    def test_house_robber(self):\n        self.assertEqual(44, house_robber([1, 2, 16, 3, 15, 3, 12, 1]))\n\n\nclass TestJobScheduling(unittest.TestCase):\n    def test_job_scheduling(self):\n        job1, job2 = Job(1, 3, 2), Job(2, 3, 4)\n        self.assertEqual(4, schedule([job1, job2]))\n\n\nclass TestKnapsack(unittest.TestCase):\n    def test_get_maximum_value(self):\n        item1, item2, item3 = Item(60, 10), Item(100, 20), Item(120, 30)\n        self.assertEqual(220, get_maximum_value([item1, item2, item3], 50))\n\n        item1, item2, item3, item4 = Item(60, 5), Item(50, 3), Item(70, 4), Item(30, 2)\n        self.assertEqual(80, get_maximum_value([item1, item2, item3, item4], 5))\n\n\nclass TestLongestIncreasingSubsequence(unittest.TestCase):\n    def test_longest_increasing_subsequence(self):\n        sequence = [1, 101, 10, 2, 3, 100, 4, 6, 2]\n        self.assertEqual(5, longest_increasing_subsequence(sequence))\n\n\nclass TestLongestIncreasingSubsequenceOptimized(unittest.TestCase):\n    def test_longest_increasing_subsequence_optimized(self):\n        sequence = [1, 101, 10, 2, 3, 100, 4, 6, 2]\n        self.assertEqual(5, longest_increasing_subsequence(sequence))\n\n\nclass TestLongestIncreasingSubsequenceOptimized2(unittest.TestCase):\n    def test_longest_increasing_subsequence_optimized2(self):\n        sequence = [1, 101, 10, 2, 3, 100, 4, 6, 2]\n        self.assertEqual(5, longest_increasing_subsequence(sequence))\n\n\nclass TestIntDivide(unittest.TestCase):\n    def test_int_divide(self):\n        self.assertEqual(5, int_divide(4))\n        self.assertEqual(42, int_divide(10))\n        self.assertEqual(204226, int_divide(50))\n\n\nclass TestDpKFactor(unittest.TestCase):\n    def test_kfactor(self):\n        # Test 1\n        n1 = 4\n        k1 = 1\n        self.assertEqual(find_k_factor(n1, k1), 1)\n\n        # Test 2\n        n2 = 7\n        k2 = 1\n        self.assertEqual(find_k_factor(n2, k2), 70302)\n\n        # Test 3\n        n3 = 10\n        k3 = 2\n        self.assertEqual(find_k_factor(n3, k3), 74357)\n\n        # Test 4\n        n4 = 8\n        k4 = 2\n        self.assertEqual(find_k_factor(n4, k4), 53)\n\n        # Test 5\n        n5 = 9\n        k5 = 1\n        self.assertEqual(find_k_factor(n5, k5), 71284044)\n\n\nclass TestPlantingTrees(unittest.TestCase):\n    def test_simple(self):\n        # arrange\n        trees = [0, 1, 10, 10]\n        length = 10\n        width = 1\n\n        # act\n        res = planting_trees(trees, length, width)\n\n        # assert\n        self.assertEqual(res, 2.414213562373095)\n\n    def test_simple2(self):\n        # arrange\n        trees = [0, 3, 5, 5, 6, 9]\n        length = 10\n        width = 1\n\n        # act\n        res = planting_trees(trees, length, width)\n\n        # assert\n        self.assertEqual(res, 9.28538328578604)\n\n\nclass TestRegexMatching(unittest.TestCase):\n    def test_none_0(self):\n        s = \"\"\n        p = \"\"\n        self.assertTrue(regex_matching.is_match(s, p))\n\n    def test_none_1(self):\n        s = \"\"\n        p = \"a\"\n        self.assertFalse(regex_matching.is_match(s, p))\n\n    def test_no_symbol_equal(self):\n        s = \"abcd\"\n        p = \"abcd\"\n        self.assertTrue(regex_matching.is_match(s, p))\n\n    def test_no_symbol_not_equal_0(self):\n        s = \"abcd\"\n        p = \"efgh\"\n        self.assertFalse(regex_matching.is_match(s, p))\n\n    def test_no_symbol_not_equal_1(self):\n        s = \"ab\"\n        p = \"abb\"\n        self.assertFalse(regex_matching.is_match(s, p))\n\n    def test_symbol_0(self):\n        s = \"\"\n        p = \"a*\"\n        self.assertTrue(regex_matching.is_match(s, p))\n\n    def test_symbol_1(self):\n        s = \"a\"\n        p = \"ab*\"\n        self.assertTrue(regex_matching.is_match(s, p))\n\n    def test_symbol_2(self):\n        # E.g.\n        #   s a b b\n        # p 1 0 0 0\n        # a 0 1 0 0\n        # b 0 0 1 0\n        # * 0 1 1 1\n        s = \"abb\"\n        p = \"ab*\"\n        self.assertTrue(regex_matching.is_match(s, p))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_graph.py",
    "content": "import unittest\n\nfrom algorithms.graph import (\n    Sudoku,\n    Tarjan,\n    all_pairs_shortest_path,\n    bellman_ford,\n    check_bipartite,\n    check_digraph_strongly_connected,\n    count_connected_number_of_component,\n    count_islands,\n    cycle_detection,\n    dinic,\n    edmonds_karp,\n    find_path,\n    find_path_dfs,\n    ford_fulkerson,\n    get_factors,\n    get_factors_iterative1,\n    get_factors_iterative2,\n    ladder_length,\n    maximum_flow_bfs,\n    maximum_flow_dfs,\n    maze_search,\n    num_islands_dfs,\n    pacific_atlantic,\n    path_between_two_vertices_in_digraph,\n    prims_minimum_spanning,\n    strongly_connected_components_kosaraju,\n    top_sort,\n    top_sort_recursive,\n    topological_sort,\n    walls_and_gates,\n)\nfrom algorithms.graph.dijkstra import Dijkstra\n\n\nclass TestTarjan(unittest.TestCase):\n    \"\"\"\n    Test for the file tarjan.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_tarjan_example_1(self):\n        # Graph from https://en.wikipedia.org/wiki/File:Scc.png\n        example = {\n            \"A\": [\"B\"],\n            \"B\": [\"C\", \"E\", \"F\"],\n            \"C\": [\"D\", \"G\"],\n            \"D\": [\"C\", \"H\"],\n            \"E\": [\"A\", \"F\"],\n            \"F\": [\"G\"],\n            \"G\": [\"F\"],\n            \"H\": [\"D\", \"G\"],\n        }\n\n        g = Tarjan(example)\n        self.assertEqual(g.sccs, [[\"F\", \"G\"], [\"C\", \"D\", \"H\"], [\"A\", \"B\", \"E\"]])\n\n    def test_tarjan_example_2(self):\n        # Graph from https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#/media/File:Tarjan%27s_Algorithm_Animation.gif\n        example = {\n            \"A\": [\"E\"],\n            \"B\": [\"A\"],\n            \"C\": [\"B\", \"D\"],\n            \"D\": [\"C\"],\n            \"E\": [\"B\"],\n            \"F\": [\"B\", \"E\", \"G\"],\n            \"G\": [\"F\", \"C\"],\n            \"H\": [\"G\", \"H\", \"D\"],\n        }\n\n        g = Tarjan(example)\n        self.assertEqual(g.sccs, [[\"A\", \"B\", \"E\"], [\"C\", \"D\"], [\"F\", \"G\"], [\"H\"]])\n\n\nclass TestCheckBipartite(unittest.TestCase):\n    def test_check_bipartite(self):\n        adj_list_1 = [[0, 0, 1], [0, 0, 1], [1, 1, 0]]\n        self.assertEqual(True, check_bipartite(adj_list_1))\n        adj_list_2 = [[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]\n        self.assertEqual(True, check_bipartite(adj_list_2))\n        adj_list_3 = [[0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 1], [0, 1, 1, 0]]\n        self.assertEqual(False, check_bipartite(adj_list_3))\n\n\nclass TestDijkstra(unittest.TestCase):\n    def test_dijkstra(self):\n        g = Dijkstra(9)\n        g.graph = [\n            [0, 4, 0, 0, 0, 0, 0, 8, 0],\n            [4, 0, 8, 0, 0, 0, 0, 11, 0],\n            [0, 8, 0, 7, 0, 4, 0, 0, 2],\n            [0, 0, 7, 0, 9, 14, 0, 0, 0],\n            [0, 0, 0, 9, 0, 10, 0, 0, 0],\n            [0, 0, 4, 14, 10, 0, 2, 0, 0],\n            [0, 0, 0, 0, 0, 2, 0, 1, 6],\n            [8, 11, 0, 0, 0, 0, 1, 0, 7],\n            [0, 0, 2, 0, 0, 0, 6, 7, 0],\n        ]\n\n        self.assertEqual(g.dijkstra(0), [0, 4, 12, 19, 21, 11, 9, 8, 14])\n\n\nclass TestMaximumFlow(unittest.TestCase):\n    \"\"\"\n    Test for the file maximum_flow.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_ford_fulkerson(self):\n        capacity = [\n            [0, 10, 10, 0, 0, 0, 0],\n            [0, 0, 2, 0, 4, 8, 0],\n            [0, 0, 0, 0, 0, 9, 0],\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 10],\n            [0, 0, 0, 0, 6, 0, 10],\n            [0, 0, 0, 0, 0, 0, 0],\n        ]\n        self.assertEqual(19, ford_fulkerson(capacity, 0, 6))\n\n    def test_edmonds_karp(self):\n        capacity = [\n            [0, 10, 10, 0, 0, 0, 0],\n            [0, 0, 2, 0, 4, 8, 0],\n            [0, 0, 0, 0, 0, 9, 0],\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 10],\n            [0, 0, 0, 0, 6, 0, 10],\n            [0, 0, 0, 0, 0, 0, 0],\n        ]\n        self.assertEqual(19, edmonds_karp(capacity, 0, 6))\n\n    def dinic(self):\n        capacity = [\n            [0, 10, 10, 0, 0, 0, 0],\n            [0, 0, 2, 0, 4, 8, 0],\n            [0, 0, 0, 0, 0, 9, 0],\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 10],\n            [0, 0, 0, 0, 6, 0, 10],\n            [0, 0, 0, 0, 0, 0, 0],\n        ]\n        self.assertEqual(19, dinic(capacity, 0, 6))\n\n\nclass TestMaximumFlowBfs(unittest.TestCase):\n    \"\"\"\n    Test for the file def maximum_flow_bfs.py\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_maximum_flow_bfs(self):\n        graph = [\n            [0, 16, 13, 0, 0, 0],\n            [0, 0, 10, 12, 0, 0],\n            [0, 4, 0, 0, 14, 0],\n            [0, 0, 9, 0, 0, 20],\n            [0, 0, 0, 7, 0, 4],\n            [0, 0, 0, 0, 0, 0],\n        ]\n        maximum_flow = maximum_flow_bfs(graph)\n\n        self.assertEqual(maximum_flow, 23)\n\n\nclass TestMaximumFlowDfs(unittest.TestCase):\n    \"\"\"\n    Test for the file def maximum_flow_dfs.py\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_maximum_flow_dfs(self):\n        graph = [\n            [0, 16, 13, 0, 0, 0],\n            [0, 0, 10, 12, 0, 0],\n            [0, 4, 0, 0, 14, 0],\n            [0, 0, 9, 0, 0, 20],\n            [0, 0, 0, 7, 0, 4],\n            [0, 0, 0, 0, 0, 0],\n        ]\n        maximum_flow = maximum_flow_dfs(graph)\n\n        self.assertEqual(maximum_flow, 23)\n\n\nclass TestAllPairsShortestPath(unittest.TestCase):\n    def test_all_pairs_shortest_path(self):\n        graph = [\n            [0, 0.1, 0.101, 0.142, 0.277],\n            [0.465, 0, 0.191, 0.192, 0.587],\n            [0.245, 0.554, 0, 0.333, 0.931],\n            [1.032, 0.668, 0.656, 0, 0.151],\n            [0.867, 0.119, 0.352, 0.398, 0],\n        ]\n        result = all_pairs_shortest_path(graph)\n\n        self.assertEqual(\n            result,\n            [\n                [0, 0.1, 0.101, 0.142, 0.277],\n                [0.436, 0, 0.191, 0.192, 0.34299999999999997],\n                [0.245, 0.345, 0, 0.333, 0.484],\n                [0.706, 0.27, 0.46099999999999997, 0, 0.151],\n                [0.5549999999999999, 0.119, 0.31, 0.311, 0],\n            ],\n        )\n\n\nclass TestBellmanFord(unittest.TestCase):\n    def test_bellman_ford(self):\n        graph1 = {\n            \"a\": {\"b\": 6, \"e\": 7},\n            \"b\": {\"c\": 5, \"d\": -4, \"e\": 8},\n            \"c\": {\"b\": -2},\n            \"d\": {\"a\": 2, \"c\": 7},\n            \"e\": {\"b\": -3},\n        }\n        self.assertEqual(True, bellman_ford(graph1, \"a\"))\n        graph2 = {\n            \"a\": {\"d\": 3, \"e\": 4},\n            \"b\": {\"a\": 7, \"e\": 2},\n            \"c\": {\"a\": 12, \"d\": 9, \"e\": 11},\n            \"d\": {\"c\": 5, \"e\": 11},\n            \"e\": {\"a\": 7, \"b\": 5, \"d\": 1},\n        }\n        self.assertEqual(True, bellman_ford(graph2, \"a\"))\n\n\nclass TestConnectedComponentInGraph(unittest.TestCase):\n    \"\"\"\n    Class for testing different cases for connected components in graph\n    \"\"\"\n\n    def test_count_connected_components(self):\n        \"\"\"\n        Test Function that test the different cases of count connected\n        components\n         2----------0    1--------5      3\n         |\n         |\n         4\n             output = 3\n        \"\"\"\n        expected_result = 3\n        # adjacency list representation of graph\n        adj_list = [[2], [5], [0, 4], [], [2], [1]]\n\n        size = 5\n        result = count_connected_number_of_component.count_components(adj_list, size)\n        self.assertEqual(result, expected_result)\n\n    def test_connected_components_with_empty_graph(self):\n        \"\"\"\n        input :\n        output : 0\n        \"\"\"\n        adj_list = [[]]\n        expected_result = 0\n        size = 0\n        result = count_connected_number_of_component.count_components(adj_list, size)\n        self.assertEqual(result, expected_result)\n\n    def test_connected_components_without_edges_graph(self):\n        \"\"\"\n        input : 0          2             3          4\n        output : 4\n        \"\"\"\n        adj_list = [[0], [], [2], [3], [4]]\n        size = 4\n        expected_result = 4\n        result = count_connected_number_of_component.count_components(adj_list, size)\n        self.assertEqual(result, expected_result)\n\n\nclass PrimsMinimumSpanning(unittest.TestCase):\n    def test_prim_spanning(self):\n        graph1 = {\n            1: [[3, 2], [8, 3]],\n            2: [[3, 1], [5, 4]],\n            3: [[8, 1], [2, 4], [4, 5]],\n            4: [[5, 2], [2, 3], [6, 5]],\n            5: [[4, 3], [6, 4]],\n        }\n        self.assertEqual(14, prims_minimum_spanning(graph1))\n        graph2 = {\n            1: [[7, 2], [6, 4]],\n            2: [[7, 1], [9, 4], [6, 3]],\n            3: [[8, 4], [6, 2]],\n            4: [[6, 1], [9, 2], [8, 3]],\n        }\n        self.assertEqual(19, prims_minimum_spanning(graph2))\n\n\nclass TestDigraphStronglyConnected(unittest.TestCase):\n    def test_digraph_strongly_connected(self):\n        g1 = check_digraph_strongly_connected.Graph(5)\n        g1.add_edge(0, 1)\n        g1.add_edge(1, 2)\n        g1.add_edge(2, 3)\n        g1.add_edge(3, 0)\n        g1.add_edge(2, 4)\n        g1.add_edge(4, 2)\n        self.assertTrue(g1.is_strongly_connected())\n\n        g2 = check_digraph_strongly_connected.Graph(4)\n        g2.add_edge(0, 1)\n        g2.add_edge(1, 2)\n        g2.add_edge(2, 3)\n        self.assertFalse(g2.is_strongly_connected())\n\n\nclass TestCycleDetection(unittest.TestCase):\n    def test_cycle_detection_with_cycle(self):\n        graph = {\n            \"A\": [\"B\", \"C\"],\n            \"B\": [\"D\"],\n            \"C\": [\"F\"],\n            \"D\": [\"E\", \"F\"],\n            \"E\": [\"B\"],\n            \"F\": [],\n        }\n        self.assertTrue(cycle_detection.contains_cycle(graph))\n\n    def test_cycle_detection_with_no_cycle(self):\n        graph = {\n            \"A\": [\"B\", \"C\"],\n            \"B\": [\"D\", \"E\"],\n            \"C\": [\"F\"],\n            \"D\": [\"E\"],\n            \"E\": [],\n            \"F\": [],\n        }\n        self.assertFalse(cycle_detection.contains_cycle(graph))\n\n\nclass TestFindPath(unittest.TestCase):\n    def test_find_all_paths(self):\n        graph = {\n            \"A\": [\"B\", \"C\"],\n            \"B\": [\"C\", \"D\"],\n            \"C\": [\"D\", \"F\"],\n            \"D\": [\"C\"],\n            \"E\": [\"F\"],\n            \"F\": [\"C\"],\n        }\n\n        paths = find_path.find_all_path(graph, \"A\", \"F\")\n        print(paths)\n        self.assertEqual(\n            sorted(paths),\n            sorted(\n                [\n                    [\"A\", \"C\", \"F\"],\n                    [\"A\", \"B\", \"C\", \"F\"],\n                    [\"A\", \"B\", \"D\", \"C\", \"F\"],\n                ]\n            ),\n        )\n\n\nclass TestPathBetweenTwoVertices(unittest.TestCase):\n    def test_node_is_reachable(self):\n        g = path_between_two_vertices_in_digraph.Graph(4)\n        g.add_edge(0, 1)\n        g.add_edge(0, 2)\n        g.add_edge(1, 2)\n        g.add_edge(2, 0)\n        g.add_edge(2, 3)\n        g.add_edge(3, 3)\n\n        self.assertTrue(g.is_reachable(1, 3))\n        self.assertFalse(g.is_reachable(3, 1))\n\n\nclass TestStronglyConnectedComponentsKosaraju(unittest.TestCase):\n    def test_kosaraju_algorithm(self):\n        vertices = 6\n        adj = [[2], [0], [3], [1, 4], [5], [4]]\n\n        result = (\n            strongly_connected_components_kosaraju.Kosaraju()\n            .kosaraju(vertices, adj)\n        )\n\n        # Expected result: 2 strongly connected components\n        self.assertEqual(result, 2)\n\n\n# --- Tests merged from test_bfs.py ---\n\n\nclass TestCountIslandsBfs(unittest.TestCase):\n    def test_count_islands(self):\n        grid_1 = [[1, 1, 1, 1, 0], [1, 1, 0, 1, 0], [1, 1, 0, 0, 0], [0, 0, 0, 0, 0]]\n        self.assertEqual(1, count_islands(grid_1))\n        grid_2 = [[1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1]]\n        self.assertEqual(3, count_islands(grid_2))\n        grid_3 = [\n            [1, 1, 1, 0, 0, 0],\n            [1, 1, 0, 0, 0, 0],\n            [1, 0, 0, 0, 0, 1],\n            [0, 0, 1, 1, 0, 1],\n            [0, 0, 1, 1, 0, 0],\n        ]\n        self.assertEqual(3, count_islands(grid_3))\n        grid_4 = [\n            [1, 1, 0, 0, 1, 1],\n            [0, 0, 1, 1, 0, 0],\n            [0, 0, 0, 0, 0, 1],\n            [1, 1, 1, 1, 0, 0],\n        ]\n        self.assertEqual(5, count_islands(grid_4))\n\n\nclass TestMazeSearchBfs(unittest.TestCase):\n    def test_maze_search(self):\n        grid_1 = [\n            [1, 0, 1, 1, 1, 1],\n            [1, 0, 1, 0, 1, 0],\n            [1, 0, 1, 0, 1, 1],\n            [1, 1, 1, 0, 1, 1],\n        ]\n        self.assertEqual(14, maze_search(grid_1))\n        grid_2 = [[1, 0, 0], [0, 1, 1], [0, 1, 1]]\n        self.assertEqual(-1, maze_search(grid_2))\n\n\nclass TestWordLadder(unittest.TestCase):\n    def test_ladder_length(self):\n\n        # hit -> hot -> dot -> dog -> cog\n        self.assertEqual(\n            5, ladder_length(\"hit\", \"cog\", [\"hot\", \"dot\", \"dog\", \"lot\", \"log\"])\n        )\n\n        # pick -> sick -> sink -> sank -> tank == 5\n        self.assertEqual(\n            5, ladder_length(\"pick\", \"tank\", [\"tock\", \"tick\", \"sank\", \"sink\", \"sick\"])\n        )\n\n        # live -> life == 1, no matter what is the word_list.\n        self.assertEqual(1, ladder_length(\"live\", \"life\", [\"hoho\", \"luck\"]))\n\n        # 0 length from ate -> ate\n        self.assertEqual(0, ladder_length(\"ate\", \"ate\", []))\n\n        # not possible to reach !\n        self.assertEqual(-1, ladder_length(\"rahul\", \"coder\", [\"blahh\", \"blhah\"]))\n\n\n# --- Tests merged from test_dfs.py ---\n\n\nclass TestAllFactors(unittest.TestCase):\n    def test_get_factors(self):\n        self.assertEqual(\n            [[2, 16], [2, 2, 8], [2, 2, 2, 4], [2, 2, 2, 2, 2], [2, 4, 4], [4, 8]],\n            get_factors(32),\n        )\n\n    def test_get_factors_iterative1(self):\n        self.assertEqual(\n            [[2, 16], [4, 8], [2, 2, 8], [2, 4, 4], [2, 2, 2, 4], [2, 2, 2, 2, 2]],\n            get_factors_iterative1(32),\n        )\n\n    def test_get_factors_iterative2(self):\n        self.assertEqual(\n            [[2, 2, 2, 2, 2], [2, 2, 2, 4], [2, 2, 8], [2, 4, 4], [2, 16], [4, 8]],\n            get_factors_iterative2(32),\n        )\n\n\nclass TestCountIslandsDfs(unittest.TestCase):\n    def test_num_islands(self):\n        self.assertEqual(\n            1,\n            num_islands_dfs(\n                [[1, 1, 1, 1, 0], [1, 1, 0, 1, 0], [1, 1, 0, 0, 0], [0, 0, 0, 0, 0]]\n            ),\n        )\n        self.assertEqual(\n            3,\n            num_islands_dfs(\n                [[1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1]]\n            ),\n        )\n\n\nclass TestPacificAtlantic(unittest.TestCase):\n    def test_pacific_atlantic(self):\n        self.assertEqual(\n            [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]],\n            pacific_atlantic(\n                [\n                    [1, 2, 2, 3, 5],\n                    [3, 2, 3, 4, 4],\n                    [2, 4, 5, 3, 1],\n                    [6, 7, 1, 4, 5],\n                    [5, 1, 1, 2, 4],\n                ]\n            ),\n        )\n\n\nclass TestSudoku(unittest.TestCase):\n    def test_sudoku_solver(self):\n        board = [[\"5\", \"3\", \".\"], [\"6\", \".\", \".\"], [\".\", \"9\", \"8\"]]\n        test_obj = Sudoku(board, 3, 3)\n        test_obj.solve()\n        self.assertEqual(\n            [[\"5\", \"3\", \"1\"], [\"6\", \"1\", \"2\"], [\"1\", \"9\", \"8\"]], test_obj.board\n        )\n\n\nclass TestWallsAndGates(unittest.TestCase):\n    def test_walls_and_gates(self):\n        rooms = [\n            [float(\"inf\"), -1, 0, float(\"inf\")],\n            [float(\"inf\"), float(\"inf\"), float(\"inf\"), -1],\n            [float(\"inf\"), -1, float(\"inf\"), -1],\n            [0, -1, float(\"inf\"), float(\"inf\")],\n        ]\n        walls_and_gates(rooms)\n        self.assertEqual(\n            [[3, -1, 0, 1], [2, 2, 1, -1], [1, -1, 2, -1], [0, -1, 3, 4]], rooms\n        )\n\n\nclass TestMazeSearchDfs(unittest.TestCase):\n    def test_maze_search(self):\n        maze_1 = [\n            [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],\n            [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1],\n        ]\n        self.assertEqual(37, find_path_dfs(maze_1))\n        maze_2 = [\n            [1, 0, 1, 1, 1, 1],\n            [1, 0, 1, 0, 1, 0],\n            [1, 0, 1, 0, 1, 1],\n            [1, 1, 1, 0, 1, 1],\n        ]\n        self.assertEqual(14, find_path_dfs(maze_2))\n        maze_3 = [[1, 0, 0], [0, 1, 1], [0, 1, 1]]\n        self.assertEqual(-1, find_path_dfs(maze_3))\n\n\n# --- Tests merged from test_topological.py ---\n\n\nclass TestTopSort(unittest.TestCase):\n    def setUp(self):\n        self.depGraph = {\n            \"a\": [\"b\"],\n            \"b\": [\"c\"],\n            \"c\": [\"e\"],\n            \"e\": [\"g\"],\n            \"d\": [],\n            \"f\": [\"e\", \"d\"],\n            \"g\": [],\n        }\n\n    def test_topsort(self):\n        res = top_sort_recursive(self.depGraph)\n        self.assertTrue(res.index(\"g\") < res.index(\"e\"))\n        res = top_sort(self.depGraph)\n        self.assertTrue(res.index(\"g\") < res.index(\"e\"))\n\n\nclass TestTopologicalSort(unittest.TestCase):\n    def test_simple_dag(self):\n        vertices = 6\n        edges = [(5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1)]\n\n        order = topological_sort(vertices, edges)\n\n        # Verify correct length\n        self.assertEqual(len(order), vertices)\n\n        # Verify constraints\n        position = {node: i for i, node in enumerate(order)}\n        for u, v in edges:\n            self.assertLess(position[u], position[v])\n\n    def test_single_vertex(self):\n        vertices = 1\n        edges = []\n\n        order = topological_sort(vertices, edges)\n        self.assertEqual(order, [0])\n\n    def test_disconnected_graph(self):\n        vertices = 4\n        edges = [(0, 1), (2, 3)]\n\n        order = topological_sort(vertices, edges)\n\n        self.assertEqual(len(order), vertices)\n\n        position = {node: i for i, node in enumerate(order)}\n        for u, v in edges:\n            self.assertLess(position[u], position[v])\n\n    def test_no_edges(self):\n        vertices = 5\n        edges = []\n\n        order = topological_sort(vertices, edges)\n\n        self.assertEqual(len(order), vertices)\n        self.assertCountEqual(order, [0, 1, 2, 3, 4])\n\n    def test_cycle_detection(self):\n        vertices = 3\n        edges = [(0, 1), (1, 2), (2, 0)]\n\n        with self.assertRaises(ValueError):\n            topological_sort(vertices, edges)\n\n    def test_self_loop_cycle(self):\n        vertices = 2\n        edges = [(0, 0)]\n\n        with self.assertRaises(ValueError):\n            topological_sort(vertices, edges)\n"
  },
  {
    "path": "tests/test_greedy.py",
    "content": "import unittest\n\nfrom algorithms.greedy import (\n    max_contiguous_subsequence_sum,\n)\n\n\nclass TestMaxContiguousSubsequenceSum(unittest.TestCase):\n    def test_max_contiguous_subsequence_sum(self):\n        arr1 = [-2, 3, 8, -1, 4]\n        arr2 = [-1, 1, 0]\n        arr3 = [-1, -3, -4]\n        arr4 = [-2, 3, 8, -12, 8, 4]\n\n        self.assertEqual(max_contiguous_subsequence_sum(arr1), 14)\n        self.assertEqual(max_contiguous_subsequence_sum(arr2), 1)\n        self.assertEqual(max_contiguous_subsequence_sum(arr3), -1)\n        self.assertEqual(max_contiguous_subsequence_sum(arr4), 12)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_heap.py",
    "content": "import unittest\n\nfrom algorithms.heap import BinaryHeap, get_skyline, k_closest, max_sliding_window\n\n\nclass TestBinaryHeap(unittest.TestCase):\n    \"\"\"\n    Test suite for the binary_heap data structures\n    \"\"\"\n\n    def setUp(self):\n        self.min_heap = BinaryHeap()\n        self.min_heap.insert(4)\n        self.min_heap.insert(50)\n        self.min_heap.insert(7)\n        self.min_heap.insert(55)\n        self.min_heap.insert(90)\n        self.min_heap.insert(87)\n\n    def test_insert(self):\n        # Before insert 2: [0, 4, 50, 7, 55, 90, 87]\n        # After insert:  [0, 2, 50, 4, 55, 90, 87, 7]\n        self.min_heap.insert(2)\n        self.assertEqual([0, 2, 50, 4, 55, 90, 87, 7], self.min_heap.heap)\n        self.assertEqual(7, self.min_heap.current_size)\n\n    def test_remove_min(self):\n        ret = self.min_heap.remove_min()\n        # Before remove_min : [0, 4, 50, 7, 55, 90, 87]\n        # After remove_min: [7, 50, 87, 55, 90]\n        # Test return value\n        self.assertEqual(4, ret)\n        self.assertEqual([0, 7, 50, 87, 55, 90], self.min_heap.heap)\n        self.assertEqual(5, self.min_heap.current_size)\n\n\nclass TestSuite(unittest.TestCase):\n    def test_get_skyline(self):\n        buildings = [[2, 9, 10], [3, 7, 15], [5, 12, 12], [15, 20, 10], [19, 24, 8]]\n        # Expect output\n        output = [[2, 10], [3, 15], [7, 12], [12, 0], [15, 10], [20, 8], [24, 0]]\n        self.assertEqual(output, get_skyline(buildings))\n\n    def test_max_sliding_window(self):\n        nums = [1, 3, -1, -3, 5, 3, 6, 7]\n        self.assertEqual([3, 3, 5, 5, 6, 7], max_sliding_window(nums, 3))\n\n    def test_k_closest_points(self):\n        points = [(1, 0), (2, 3), (5, 2), (1, 1), (2, 8), (10, 2), (-1, 0), (-2, -2)]\n        self.assertEqual([(-1, 0), (1, 0)], k_closest(points, 2))\n        self.assertEqual([(1, 1), (-1, 0), (1, 0)], k_closest(points, 3))\n        self.assertEqual([(-2, -2), (1, 1), (1, 0), (-1, 0)], k_closest(points, 4))\n        self.assertEqual(\n            [(10, 2), (2, 8), (5, 2), (-2, -2), (2, 3), (1, 0), (-1, 0), (1, 1)],\n            k_closest(points, 8),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_issue_fixes.py",
    "content": "\"\"\"Tests for algorithms added to resolve open GitHub issues.\"\"\"\n\nfrom __future__ import annotations\n\nimport unittest\n\nfrom algorithms.common.tree_node import TreeNode\nfrom algorithms.data_structures import SqrtDecomposition\nfrom algorithms.graph.dijkstra_heapq import dijkstra\nfrom algorithms.math.goldbach import goldbach, verify_goldbach\nfrom algorithms.tree.binary_tree_views import (\n    bottom_view,\n    left_view,\n    right_view,\n    top_view,\n)\n\n# ── Dijkstra with priority queue (#565) ────────────────────────────────\n\n\nclass TestDijkstraHeapq(unittest.TestCase):\n    \"\"\"Tests for the heap-based Dijkstra implementation.\"\"\"\n\n    def setUp(self):\n        self.graph = {\n            \"s\": {\"a\": 2, \"b\": 1},\n            \"a\": {\"s\": 3, \"b\": 4, \"c\": 8},\n            \"b\": {\"s\": 4, \"a\": 2, \"d\": 2},\n            \"c\": {\"a\": 2, \"d\": 7, \"t\": 4},\n            \"d\": {\"b\": 1, \"c\": 11, \"t\": 5},\n            \"t\": {\"c\": 3, \"d\": 5},\n        }\n\n    def test_shortest_path(self):\n        dist, path = dijkstra(self.graph, \"s\", \"t\")\n        self.assertEqual(dist, 8)\n        self.assertEqual(path, [\"s\", \"b\", \"d\", \"t\"])\n\n    def test_same_source_and_target(self):\n        dist, path = dijkstra(self.graph, \"s\", \"s\")\n        self.assertEqual(dist, 0)\n        self.assertEqual(path, [\"s\"])\n\n    def test_direct_neighbor(self):\n        dist, path = dijkstra(self.graph, \"s\", \"b\")\n        self.assertEqual(dist, 1)\n        self.assertEqual(path, [\"s\", \"b\"])\n\n    def test_unreachable_target(self):\n        graph = {\"a\": {\"b\": 1}, \"b\": {}, \"c\": {}}\n        dist, path = dijkstra(graph, \"a\", \"c\")\n        self.assertEqual(dist, float(\"inf\"))\n        self.assertEqual(path, [])\n\n    def test_single_node(self):\n        graph = {\"a\": {}}\n        dist, path = dijkstra(graph, \"a\", \"a\")\n        self.assertEqual(dist, 0)\n        self.assertEqual(path, [\"a\"])\n\n    def test_triangle(self):\n        graph = {\"a\": {\"b\": 1, \"c\": 4}, \"b\": {\"c\": 2}, \"c\": {}}\n        dist, path = dijkstra(graph, \"a\", \"c\")\n        self.assertEqual(dist, 3)\n        self.assertEqual(path, [\"a\", \"b\", \"c\"])\n\n\n# ── Goldbach's conjecture (#908) ────────────────────────────────────────\n\n\nclass TestGoldbach(unittest.TestCase):\n    \"\"\"Tests for Goldbach's conjecture decomposition.\"\"\"\n\n    def test_small_even(self):\n        self.assertEqual(goldbach(4), (2, 2))\n\n    def test_goldbach_28(self):\n        p, q = goldbach(28)\n        self.assertEqual(p + q, 28)\n        self.assertTrue(p <= q)\n\n    def test_goldbach_100(self):\n        p, q = goldbach(100)\n        self.assertEqual(p + q, 100)\n\n    def test_goldbach_large(self):\n        p, q = goldbach(1000)\n        self.assertEqual(p + q, 1000)\n\n    def test_odd_raises(self):\n        with self.assertRaises(ValueError):\n            goldbach(7)\n\n    def test_two_raises(self):\n        with self.assertRaises(ValueError):\n            goldbach(2)\n\n    def test_negative_raises(self):\n        with self.assertRaises(ValueError):\n            goldbach(-4)\n\n    def test_verify_range(self):\n        self.assertTrue(verify_goldbach(200))\n\n\n# ── Binary tree views (#829) ────────────────────────────────────────────\n\n\nclass TestBinaryTreeViews(unittest.TestCase):\n    \"\"\"Tests for left/right/top/bottom tree views.\"\"\"\n\n    def setUp(self):\n        \"\"\"Build a tree:\n                1\n               / \\\\\n              2   3\n             / \\\\   \\\\\n            4   5   6\n        \"\"\"\n        self.root = TreeNode(\n            1,\n            TreeNode(2, TreeNode(4), TreeNode(5)),\n            TreeNode(3, None, TreeNode(6)),\n        )\n\n    def test_left_view(self):\n        self.assertEqual(left_view(self.root), [1, 2, 4])\n\n    def test_right_view(self):\n        self.assertEqual(right_view(self.root), [1, 3, 6])\n\n    def test_top_view(self):\n        self.assertEqual(top_view(self.root), [4, 2, 1, 3, 6])\n\n    def test_bottom_view(self):\n        self.assertEqual(bottom_view(self.root), [4, 2, 5, 3, 6])\n\n    def test_empty_tree(self):\n        self.assertEqual(left_view(None), [])\n        self.assertEqual(right_view(None), [])\n        self.assertEqual(top_view(None), [])\n        self.assertEqual(bottom_view(None), [])\n\n    def test_single_node(self):\n        root = TreeNode(42)\n        self.assertEqual(left_view(root), [42])\n        self.assertEqual(right_view(root), [42])\n        self.assertEqual(top_view(root), [42])\n        self.assertEqual(bottom_view(root), [42])\n\n    def test_left_skewed(self):\n        \"\"\"\n            1\n           /\n          2\n         /\n        3\n        \"\"\"\n        root = TreeNode(1, TreeNode(2, TreeNode(3)))\n        self.assertEqual(left_view(root), [1, 2, 3])\n        self.assertEqual(right_view(root), [1, 2, 3])\n\n    def test_right_skewed(self):\n        \"\"\"\n        1\n         \\\\\n          2\n           \\\\\n            3\n        \"\"\"\n        root = TreeNode(1, None, TreeNode(2, None, TreeNode(3)))\n        self.assertEqual(left_view(root), [1, 2, 3])\n        self.assertEqual(right_view(root), [1, 2, 3])\n\n\n# ── Square root decomposition (#651) ────────────────────────────────────\n\n\nclass TestSqrtDecomposition(unittest.TestCase):\n    \"\"\"Tests for square root decomposition range queries.\"\"\"\n\n    def test_full_range_sum(self):\n        sd = SqrtDecomposition([1, 2, 3, 4, 5, 6, 7, 8, 9])\n        self.assertEqual(sd.query(0, 8), 45)\n\n    def test_partial_range(self):\n        sd = SqrtDecomposition([1, 2, 3, 4, 5, 6, 7, 8, 9])\n        self.assertEqual(sd.query(2, 5), 18)  # 3+4+5+6\n\n    def test_single_element(self):\n        sd = SqrtDecomposition([10, 20, 30])\n        self.assertEqual(sd.query(1, 1), 20)\n\n    def test_update(self):\n        sd = SqrtDecomposition([1, 2, 3, 4, 5])\n        sd.update(2, 10)\n        self.assertEqual(sd.query(0, 4), 22)  # 1+2+10+4+5\n\n    def test_update_full_range(self):\n        sd = SqrtDecomposition([1, 2, 3, 4, 5, 6, 7, 8, 9])\n        sd.update(4, 10)\n        self.assertEqual(sd.query(0, 8), 50)\n\n    def test_multiple_updates(self):\n        sd = SqrtDecomposition([1, 1, 1, 1, 1])\n        sd.update(0, 5)\n        sd.update(4, 5)\n        self.assertEqual(sd.query(0, 4), 13)  # 5+1+1+1+5\n\n    def test_out_of_range_update(self):\n        sd = SqrtDecomposition([1, 2, 3])\n        with self.assertRaises(IndexError):\n            sd.update(5, 10)\n\n    def test_invalid_query(self):\n        sd = SqrtDecomposition([1, 2, 3])\n        with self.assertRaises(IndexError):\n            sd.query(0, 5)\n\n    def test_small_array(self):\n        sd = SqrtDecomposition([42])\n        self.assertEqual(sd.query(0, 0), 42)\n        sd.update(0, 100)\n        self.assertEqual(sd.query(0, 0), 100)\n\n    def test_larger_array(self):\n        arr = list(range(1, 101))  # 1..100\n        sd = SqrtDecomposition(arr)\n        self.assertEqual(sd.query(0, 99), 5050)\n        self.assertEqual(sd.query(0, 9), 55)  # 1+2+...+10\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_iterative_segment_tree.py",
    "content": "import unittest\nfrom functools import reduce\n\nfrom algorithms.data_structures.iterative_segment_tree import SegmentTree\n\n\ndef gcd(a, b):\n    if b == 0:\n        return a\n    return gcd(b, a % b)\n\n\nclass TestSegmentTree(unittest.TestCase):\n    \"\"\"\n    Test for the Iterative Segment Tree data structure\n    \"\"\"\n\n    def test_segment_tree_creation(self):\n        arr = [2, 4, 3, 6, 8, 9, 3]\n        max_segment_tree = SegmentTree(arr, max)\n        min_segment_tree = SegmentTree(arr, min)\n        sum_segment_tree = SegmentTree(arr, lambda a, b: a + b)\n        gcd_segment_tree = SegmentTree(arr, gcd)\n        self.assertEqual(\n            max_segment_tree.tree, [None, 9, 8, 9, 4, 8, 9, 2, 4, 3, 6, 8, 9, 3]\n        )\n        self.assertEqual(\n            min_segment_tree.tree, [None, 2, 3, 2, 3, 6, 3, 2, 4, 3, 6, 8, 9, 3]\n        )\n        self.assertEqual(\n            sum_segment_tree.tree, [None, 35, 21, 14, 7, 14, 12, 2, 4, 3, 6, 8, 9, 3]\n        )\n        self.assertEqual(\n            gcd_segment_tree.tree, [None, 1, 1, 1, 1, 2, 3, 2, 4, 3, 6, 8, 9, 3]\n        )\n\n    def test_max_segment_tree(self):\n        arr = [-1, 1, 10, 2, 9, -3, 8, 4, 7, 5, 6, 0]\n        self.__test_all_segments(arr, max)\n\n    def test_min_segment_tree(self):\n        arr = [1, 10, -2, 9, -3, 8, 4, -7, 5, 6, 11, -12]\n        self.__test_all_segments(arr, min)\n\n    def test_sum_segment_tree(self):\n        arr = [1, 10, 2, 9, 3, 8, 4, 7, 5, 6, -11, -12]\n        self.__test_all_segments(arr, lambda a, b: a + b)\n\n    def test_gcd_segment_tree(self):\n        arr = [1, 10, 2, 9, 3, 8, 4, 7, 5, 6, 11, 12, 14]\n        self.__test_all_segments(arr, gcd)\n\n    def test_max_segment_tree_with_updates(self):\n        arr = [-1, 1, 10, 2, 9, -3, 8, 4, 7, 5, 6, 0]\n        updates = {\n            0: 1,\n            1: 2,\n            2: 3,\n            3: 4,\n            4: 5,\n            5: 6,\n            6: 7,\n            7: 8,\n            8: 9,\n            9: 10,\n            10: 11,\n            11: 12,\n        }\n        self.__test_all_segments_with_updates(arr, max, updates)\n\n    def test_min_segment_tree_with_updates(self):\n        arr = [1, 10, -2, 9, -3, 8, 4, -7, 5, 6, 11, -12]\n        updates = {\n            0: 7,\n            1: 2,\n            2: 6,\n            3: -14,\n            4: 5,\n            5: 4,\n            6: 7,\n            7: -10,\n            8: 9,\n            9: 10,\n            10: 12,\n            11: 1,\n        }\n        self.__test_all_segments_with_updates(arr, min, updates)\n\n    def test_sum_segment_tree_with_updates(self):\n        arr = [1, 10, 2, 9, 3, 8, 4, 7, 5, 6, -11, -12]\n        updates = {\n            0: 12,\n            1: 11,\n            2: 10,\n            3: 9,\n            4: 8,\n            5: 7,\n            6: 6,\n            7: 5,\n            8: 4,\n            9: 3,\n            10: 2,\n            11: 1,\n        }\n        self.__test_all_segments_with_updates(arr, lambda a, b: a + b, updates)\n\n    def test_gcd_segment_tree_with_updates(self):\n        arr = [1, 10, 2, 9, 3, 8, 4, 7, 5, 6, 11, 12, 14]\n        updates = {\n            0: 4,\n            1: 2,\n            2: 3,\n            3: 9,\n            4: 21,\n            5: 7,\n            6: 4,\n            7: 4,\n            8: 2,\n            9: 5,\n            10: 17,\n            11: 12,\n            12: 3,\n        }\n        self.__test_all_segments_with_updates(arr, gcd, updates)\n\n    def __test_all_segments(self, arr, fnc):\n        \"\"\"\n        Test all possible segments in the tree\n        :param arr: array to test\n        :param fnc: function of the segment tpree\n        \"\"\"\n        segment_tree = SegmentTree(arr, fnc)\n        self.__test_segments_helper(segment_tree, fnc, arr)\n\n    def __test_all_segments_with_updates(self, arr, fnc, upd):\n        \"\"\"\n        Test all possible segments in the tree with updates\n        :param arr: array to test\n        :param fnc: function of the segment tree\n        :param upd: updates to test\n        \"\"\"\n        segment_tree = SegmentTree(arr, fnc)\n        for index, value in upd.items():\n            arr[index] = value\n            segment_tree.update(index, value)\n            self.__test_segments_helper(segment_tree, fnc, arr)\n\n    def __test_segments_helper(self, seg_tree, fnc, arr):\n        for i in range(0, len(arr)):\n            for j in range(i, len(arr)):\n                range_value = reduce(fnc, arr[i : j + 1])\n                self.assertEqual(seg_tree.query(i, j), range_value)\n"
  },
  {
    "path": "tests/test_linked_list.py",
    "content": "import unittest\n\nfrom algorithms.linked_list import (\n    RandomListNode,\n    copy_random_pointer_v1,\n    copy_random_pointer_v2,\n    is_cyclic,\n    is_palindrome,\n    is_palindrome_dict,\n    is_palindrome_stack,\n    is_sorted,\n    merge_two_list,\n    merge_two_list_recur,\n    remove_range,\n    reverse_list,\n    reverse_list_recursive,\n    rotate_right,\n    swap_pairs,\n)\n\n\nclass Node:\n    def __init__(self, x):\n        self.val = x\n        self.next = None\n\n\n# Convert from linked list Node to list for testing\ndef convert(head):\n    ret = []\n    if head:\n        current = head\n        while current:\n            ret.append(current.val)\n            current = current.next\n    return ret\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        # list test for palindrome\n        self.l = Node(\"A\")\n        self.l.next = Node(\"B\")\n        self.l.next.next = Node(\"C\")\n        self.l.next.next.next = Node(\"B\")\n        self.l.next.next.next.next = Node(\"A\")\n\n        self.l1 = Node(\"A\")\n        self.l1.next = Node(\"B\")\n        self.l1.next.next = Node(\"C\")\n        self.l1.next.next.next = Node(\"B\")\n\n    def test_reverse_list(self):\n        head = Node(1)\n        head.next = Node(2)\n        head.next.next = Node(3)\n        head.next.next.next = Node(4)\n        self.assertEqual([4, 3, 2, 1], convert(reverse_list(head)))\n        head = Node(1)\n        head.next = Node(2)\n        head.next.next = Node(3)\n        head.next.next.next = Node(4)\n        self.assertEqual([4, 3, 2, 1], convert(reverse_list_recursive(head)))\n\n    def test_is_sorted(self):\n        head = Node(-2)\n        head.next = Node(2)\n        head.next.next = Node(2)\n        head.next.next.next = Node(4)\n        head.next.next.next.next = Node(9)\n        # head -> -2 -> 2 -> 2 -> 4 -> 9\n        self.assertTrue(is_sorted(head))\n        head = Node(1)\n        head.next = Node(2)\n        head.next.next = Node(8)\n        head.next.next.next = Node(4)\n        head.next.next.next.next = Node(6)\n        # head -> 1 -> 2 -> 8 -> 4 -> 6\n        self.assertFalse(is_sorted(head))\n\n    def test_remove_range(self):\n        # Test case: middle case.\n        head = Node(0)\n        head.next = Node(1)\n        head.next.next = Node(2)\n        head.next.next.next = Node(3)\n        head.next.next.next.next = Node(4)\n        # Expect output: 0 4\n        self.assertEqual([0, 4], convert(remove_range(head, 1, 3)))\n\n        # Test case: taking out the front node\n        head = Node(0)\n        head.next = Node(1)\n        head.next.next = Node(2)\n        head.next.next.next = Node(3)\n        head.next.next.next.next = Node(4)\n        # Expect output: 2 3 4\n        self.assertEqual([2, 3, 4], convert(remove_range(head, 0, 1)))\n\n        # Test case: removing all the nodes\n        head = Node(0)\n        head.next = Node(1)\n        head.next.next = Node(2)\n        head.next.next.next = Node(3)\n        head.next.next.next.next = Node(4)\n        self.assertEqual([], convert(remove_range(head, 0, 7)))\n\n    def test_swap_in_pairs(self):\n        head = Node(1)\n        head.next = Node(2)\n        head.next.next = Node(3)\n        head.next.next.next = Node(4)\n        # Expect output : 2 --> 1 --> 4 --> 3\n        self.assertEqual([2, 1, 4, 3], convert(swap_pairs(head)))\n\n    def test_rotate_right(self):\n        # Given 1->2->3->4->5->NULL\n        head = Node(1)\n        head.next = Node(2)\n        head.next.next = Node(3)\n        head.next.next.next = Node(4)\n        head.next.next.next.next = Node(5)\n        # K = 2. Expect output: 4->5->1->2->3->NULL.\n        self.assertEqual([4, 5, 1, 2, 3], convert(rotate_right(head, 2)))\n\n    def test_is_cyclic(self):\n        # create linked list => A -> B -> C -> D -> E -> C\n        head = Node(\"A\")\n        head.next = Node(\"B\")\n        curr = head.next\n        cyclic_node = Node(\"C\")\n        curr.next = cyclic_node\n        curr = curr.next\n        curr.next = Node(\"D\")\n        curr = curr.next\n        curr.next = Node(\"E\")\n        curr = curr.next\n        curr.next = cyclic_node\n        self.assertTrue(is_cyclic(head))\n\n        # create linked list 1 -> 2 -> 3 -> 4\n        head = Node(1)\n        curr = head\n        for i in range(2, 6):\n            curr.next = Node(i)\n            curr = curr.next\n        self.assertFalse(is_cyclic(head))\n\n    def test_merge_two_list(self):\n        \"\"\"\n        Input: head1:1->2->4, head2: 1->3->4\n        Output: 1->1->2->3->4->4\n        \"\"\"\n        head1 = Node(1)\n        head1.next = Node(2)\n        head1.next.next = Node(4)\n        head2 = Node(1)\n        head2.next = Node(3)\n        head2.next.next = Node(4)\n        self.assertEqual([1, 1, 2, 3, 4, 4], convert(merge_two_list(head1, head2)))\n        # Test recursive\n        head1 = Node(1)\n        head1.next = Node(2)\n        head1.next.next = Node(4)\n        head2 = Node(1)\n        head2.next = Node(3)\n        head2.next.next = Node(4)\n        self.assertEqual(\n            [1, 1, 2, 3, 4, 4], convert(merge_two_list_recur(head1, head2))\n        )\n\n    def test_is_palindrome(self):\n        self.assertTrue(is_palindrome(self.l))\n        self.assertFalse(is_palindrome(self.l1))\n\n    def test_is_palindrome_stack(self):\n        self.assertTrue(is_palindrome_stack(self.l))\n        self.assertFalse(is_palindrome_stack(self.l1))\n\n    def test_is_palindrome_dict(self):\n        self.assertTrue(is_palindrome_dict(self.l))\n        self.assertFalse(is_palindrome_dict(self.l1))\n\n    def test_solution_0(self):\n        self._init_random_list_nodes()\n        result = copy_random_pointer_v1(self.random_list_node1)\n        self._assert_is_a_copy(result)\n\n    def test_solution_1(self):\n        self._init_random_list_nodes()\n        result = copy_random_pointer_v2(self.random_list_node1)\n        self._assert_is_a_copy(result)\n\n    def _assert_is_a_copy(self, result):\n        self.assertEqual(5, result.next.next.next.next.label)\n        self.assertEqual(4, result.next.next.next.label)\n        self.assertEqual(3, result.next.next.label)\n        self.assertEqual(2, result.next.label)\n        self.assertEqual(1, result.label)\n        self.assertEqual(3, result.next.next.next.next.random.label)\n        self.assertIsNone(result.next.next.next.random)\n        self.assertEqual(2, result.next.next.random.label)\n        self.assertEqual(5, result.next.random.label)\n        self.assertEqual(4, result.random.label)\n\n    def _init_random_list_nodes(self):\n        self.random_list_node1 = RandomListNode(1)\n        random_list_node2 = RandomListNode(2)\n        random_list_node3 = RandomListNode(3)\n        random_list_node4 = RandomListNode(4)\n        random_list_node5 = RandomListNode(5)\n\n        self.random_list_node1.next, self.random_list_node1.random = (\n            random_list_node2,\n            random_list_node4,\n        )\n        random_list_node2.next, random_list_node2.random = (\n            random_list_node3,\n            random_list_node5,\n        )\n        random_list_node3.next, random_list_node3.random = (\n            random_list_node4,\n            random_list_node2,\n        )\n        random_list_node4.next = random_list_node5\n        random_list_node5.random = random_list_node3\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_map.py",
    "content": "import unittest\n\nfrom algorithms.map import (\n    HashTable,\n    ResizableHashTable,\n    SeparateChainingHashTable,\n    is_anagram,\n    is_isomorphic,\n    longest_palindromic_subsequence,\n    word_pattern,\n)\n\n\nclass TestHashTable(unittest.TestCase):\n    def test_one_entry(self):\n        m = HashTable(10)\n        m.put(1, \"1\")\n        self.assertEqual(\"1\", m.get(1))\n\n    def test_add_entry_bigger_than_table_size(self):\n        m = HashTable(10)\n        m.put(11, \"1\")\n        self.assertEqual(\"1\", m.get(11))\n\n    def test_get_none_if_key_missing_and_hash_collision(self):\n        m = HashTable(10)\n        m.put(1, \"1\")\n        self.assertEqual(None, m.get(11))\n\n    def test_two_entries_with_same_hash(self):\n        m = HashTable(10)\n        m.put(1, \"1\")\n        m.put(11, \"11\")\n        self.assertEqual(\"1\", m.get(1))\n        self.assertEqual(\"11\", m.get(11))\n\n    def test_get_on_full_table_does_halts(self):\n        # and does not search forever\n        m = HashTable(10)\n        for i in range(10, 20):\n            m.put(i, i)\n        self.assertEqual(None, m.get(1))\n\n    def test_delete_key(self):\n        m = HashTable(10)\n        for i in range(5):\n            m.put(i, i**2)\n        m.del_(1)\n        self.assertEqual(None, m.get(1))\n        self.assertEqual(4, m.get(2))\n\n    def test_delete_key_and_reassign(self):\n        m = HashTable(10)\n        m.put(1, 1)\n        del m[1]\n        m.put(1, 2)\n        self.assertEqual(2, m.get(1))\n\n    def test_assigning_to_full_table_throws_error(self):\n        m = HashTable(3)\n        m.put(1, 1)\n        m.put(2, 2)\n        m.put(3, 3)\n        with self.assertRaises(ValueError):\n            m.put(4, 4)\n\n    def test_len_trivial(self):\n        m = HashTable(10)\n        self.assertEqual(0, len(m))\n        for i in range(10):\n            m.put(i, i)\n            self.assertEqual(i + 1, len(m))\n\n    def test_len_after_deletions(self):\n        m = HashTable(10)\n        m.put(1, 1)\n        self.assertEqual(1, len(m))\n        m.del_(1)\n        self.assertEqual(0, len(m))\n        m.put(11, 42)\n        self.assertEqual(1, len(m))\n\n    def test_resizable_hash_table(self):\n        m = ResizableHashTable()\n        self.assertEqual(ResizableHashTable.MIN_SIZE, m.size)\n        for i in range(ResizableHashTable.MIN_SIZE):\n            m.put(i, \"foo\")\n        self.assertEqual(ResizableHashTable.MIN_SIZE * 2, m.size)\n        self.assertEqual(\"foo\", m.get(1))\n        self.assertEqual(\"foo\", m.get(3))\n        self.assertEqual(\"foo\", m.get(ResizableHashTable.MIN_SIZE - 1))\n\n    def test_fill_up_the_limit(self):\n        m = HashTable(10)\n        for i in range(10):\n            m.put(i, i**2)\n        for i in range(10):\n            self.assertEqual(i**2, m.get(i))\n\n\nclass TestSeparateChainingHashTable(unittest.TestCase):\n    def test_one_entry(self):\n        m = SeparateChainingHashTable(10)\n        m.put(1, \"1\")\n        self.assertEqual(\"1\", m.get(1))\n\n    def test_two_entries_with_same_hash(self):\n        m = SeparateChainingHashTable(10)\n        m.put(1, \"1\")\n        m.put(11, \"11\")\n        self.assertEqual(\"1\", m.get(1))\n        self.assertEqual(\"11\", m.get(11))\n\n    def test_len_trivial(self):\n        m = SeparateChainingHashTable(10)\n        self.assertEqual(0, len(m))\n        for i in range(10):\n            m.put(i, i)\n            self.assertEqual(i + 1, len(m))\n\n    def test_len_after_deletions(self):\n        m = SeparateChainingHashTable(10)\n        m.put(1, 1)\n        self.assertEqual(1, len(m))\n        m.del_(1)\n        self.assertEqual(0, len(m))\n        m.put(11, 42)\n        self.assertEqual(1, len(m))\n\n    def test_delete_key(self):\n        m = SeparateChainingHashTable(10)\n        for i in range(5):\n            m.put(i, i**2)\n        m.del_(1)\n        self.assertEqual(None, m.get(1))\n        self.assertEqual(4, m.get(2))\n\n    def test_delete_key_and_reassign(self):\n        m = SeparateChainingHashTable(10)\n        m.put(1, 1)\n        del m[1]\n        m.put(1, 2)\n        self.assertEqual(2, m.get(1))\n\n    def test_add_entry_bigger_than_table_size(self):\n        m = SeparateChainingHashTable(10)\n        m.put(11, \"1\")\n        self.assertEqual(\"1\", m.get(11))\n\n    def test_get_none_if_key_missing_and_hash_collision(self):\n        m = SeparateChainingHashTable(10)\n        m.put(1, \"1\")\n        self.assertEqual(None, m.get(11))\n\n\nclass TestWordPattern(unittest.TestCase):\n    def test_word_pattern(self):\n        self.assertTrue(word_pattern(\"abba\", \"dog cat cat dog\"))\n        self.assertFalse(word_pattern(\"abba\", \"dog cat cat fish\"))\n        self.assertFalse(word_pattern(\"abba\", \"dog dog dog dog\"))\n        self.assertFalse(word_pattern(\"aaaa\", \"dog cat cat dog\"))\n\n\nclass TestIsSomorphic(unittest.TestCase):\n    def test_is_isomorphic(self):\n        self.assertTrue(is_isomorphic(\"egg\", \"add\"))\n        self.assertFalse(is_isomorphic(\"foo\", \"bar\"))\n        self.assertTrue(is_isomorphic(\"paper\", \"title\"))\n\n\nclass TestLongestPalindromicSubsequence(unittest.TestCase):\n    def test_longest_palindromic_subsequence_is_correct(self):\n        self.assertEqual(3, longest_palindromic_subsequence(\"BBABCBCAB\"))\n        self.assertEqual(4, longest_palindromic_subsequence(\"abbaeae\"))\n        self.assertEqual(7, longest_palindromic_subsequence(\"babbbababaa\"))\n        self.assertEqual(4, longest_palindromic_subsequence(\"daccandeeja\"))\n\n    def test_longest_palindromic_subsequence_is_incorrect(self):\n        self.assertNotEqual(4, longest_palindromic_subsequence(\"BBABCBCAB\"))\n        self.assertNotEqual(5, longest_palindromic_subsequence(\"abbaeae\"))\n        self.assertNotEqual(2, longest_palindromic_subsequence(\"babbbababaa\"))\n        self.assertNotEqual(1, longest_palindromic_subsequence(\"daccandeeja\"))\n\n\nclass TestIsAnagram(unittest.TestCase):\n    def test_is_anagram(self):\n        self.assertTrue(is_anagram(\"anagram\", \"nagaram\"))\n        self.assertFalse(is_anagram(\"rat\", \"car\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_math.py",
    "content": "import unittest\n\nimport pytest\n\nfrom algorithms.math import (\n    base_to_int,\n    chinese_remainder_theorem,\n    combination,\n    combination_memo,\n    cosine_similarity,\n    decimal_to_binary_ip,\n    decrypt,\n    diffie_hellman_key_exchange,\n    encrypt,\n    euler_totient,\n    extended_gcd,\n    factorial,\n    factorial_recur,\n    fft,\n    find_next_square,\n    find_next_square2,\n    find_order,\n    find_primitive_root,\n    gcd,\n    gcd_bit,\n    gen_strobogrammatic,\n    get_primes,\n    hailstone,\n    int_to_base,\n    is_prime,\n    is_strobogrammatic,\n    is_strobogrammatic2,\n    krishnamurthy_number,\n    lcm,\n    magic_number,\n    modular_exponential,\n    modular_inverse,\n    num_digits,\n    num_perfect_squares,\n    power,\n    power_recur,\n    prime_check,\n    pythagoras,\n    strobogrammatic_in_range,\n    trailing_zero,\n)\n\n\nclass TestPower(unittest.TestCase):\n    \"\"\"\n    Test for the file power.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_power(self):\n        self.assertEqual(8, power(2, 3))\n        self.assertEqual(1, power(5, 0))\n        self.assertEqual(0, power(10, 3, 5))\n        self.assertEqual(280380, power(2265, 1664, 465465))\n\n    def test_power_recur(self):\n        self.assertEqual(8, power_recur(2, 3))\n        self.assertEqual(1, power_recur(5, 0))\n        self.assertEqual(0, power_recur(10, 3, 5))\n        self.assertEqual(280380, power_recur(2265, 1664, 465465))\n\n\nclass TestBaseConversion(unittest.TestCase):\n    \"\"\"\n    Test for the file base_conversion.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_int_to_base(self):\n        self.assertEqual(\"101\", int_to_base(5, 2))\n        self.assertEqual(\"0\", int_to_base(0, 2))\n        self.assertEqual(\"FF\", int_to_base(255, 16))\n\n    def test_base_to_int(self):\n        self.assertEqual(5, base_to_int(\"101\", 2))\n        self.assertEqual(0, base_to_int(\"0\", 2))\n        self.assertEqual(255, base_to_int(\"FF\", 16))\n\n\nclass TestDecimalToBinaryIP(unittest.TestCase):\n    \"\"\"\n    Test for the file decimal_to_binary_ip.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_decimal_to_binary_ip(self):\n        self.assertEqual(\n            \"00000000.00000000.00000000.00000000\", decimal_to_binary_ip(\"0.0.0.0\")\n        )\n        self.assertEqual(\n            \"11111111.11111111.11111111.11111111\",\n            decimal_to_binary_ip(\"255.255.255.255\"),\n        )\n        self.assertEqual(\n            \"11000000.10101000.00000000.00000001\", decimal_to_binary_ip(\"192.168.0.1\")\n        )\n\n\nclass TestEulerTotient(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file euler_totient.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_euler_totient(self):\n        self.assertEqual(4, euler_totient(8))\n        self.assertEqual(12, euler_totient(21))\n        self.assertEqual(311040, euler_totient(674614))\n        self.assertEqual(2354352, euler_totient(3435145))\n\n\nclass TestExtendedGcd(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file extended_gcd.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_extended_gcd(self):\n        self.assertEqual((0, 1, 2), extended_gcd(8, 2))\n        self.assertEqual((0, 1, 17), extended_gcd(13, 17))\n\n\nclass TestGcd(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file gcd.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_gcd(self):\n        self.assertEqual(4, gcd(8, 12))\n        self.assertEqual(1, gcd(13, 17))\n\n    def test_gcd_non_integer_input(self):\n        with pytest.raises(ValueError, match=r\"Input arguments are not integers\"):\n            gcd(1.0, 5)\n            gcd(5, 6.7)\n            gcd(33.8649, 6.12312312)\n\n    def test_gcd_zero_input(self):\n        with pytest.raises(\n            ValueError, match=r\"One or more input arguments equals zero\"\n        ):\n            gcd(0, 12)\n            gcd(12, 0)\n            gcd(0, 0)\n\n    def test_gcd_negative_input(self):\n        self.assertEqual(1, gcd(-13, -17))\n        self.assertEqual(4, gcd(-8, 12))\n        self.assertEqual(8, gcd(24, -16))\n\n    def test_lcm(self):\n        self.assertEqual(24, lcm(8, 12))\n        self.assertEqual(5767, lcm(73, 79))\n\n    def test_lcm_negative_numbers(self):\n        self.assertEqual(24, lcm(-8, -12))\n        self.assertEqual(5767, lcm(73, -79))\n        self.assertEqual(1, lcm(-1, 1))\n\n    def test_lcm_zero_input(self):\n        with pytest.raises(\n            ValueError, match=r\"One or more input arguments equals zero\"\n        ):\n            lcm(0, 12)\n            lcm(12, 0)\n            lcm(0, 0)\n\n    def test_trailing_zero(self):\n        self.assertEqual(1, trailing_zero(34))\n        self.assertEqual(3, trailing_zero(40))\n\n    def test_gcd_bit(self):\n        self.assertEqual(4, gcd_bit(8, 12))\n        self.assertEqual(1, gcd(13, 17))\n\n\nclass TestGenerateStroboGrammatic(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file generate_strobogrammatic.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_gen_strobomatic(self):\n        self.assertEqual([\"88\", \"11\", \"96\", \"69\"], gen_strobogrammatic(2))\n\n    def test_strobogrammatic_in_range(self):\n        self.assertEqual(4, strobogrammatic_in_range(\"10\", \"100\"))\n\n\nclass TestIsStrobogrammatic(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file is_strobogrammatic.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_strobogrammatic(self):\n        self.assertTrue(is_strobogrammatic(\"69\"))\n        self.assertFalse(is_strobogrammatic(\"14\"))\n\n    def test_is_strobogrammatic2(self):\n        self.assertTrue(is_strobogrammatic2(\"69\"))\n        self.assertFalse(is_strobogrammatic2(\"14\"))\n\n\nclass TestModularInverse(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file modular_Exponential.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_modular_inverse(self):\n        # checks if x * x_inv == 1 (mod m)\n        self.assertEqual(1, 2 * modular_inverse.modular_inverse(2, 19) % 19)\n        self.assertEqual(1, 53 * modular_inverse.modular_inverse(53, 91) % 91)\n        self.assertEqual(\n            1, 2 * modular_inverse.modular_inverse(2, 1000000007) % 1000000007\n        )\n        self.assertRaises(ValueError, modular_inverse.modular_inverse, 2, 20)\n\n\nclass TestModularExponential(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file modular_Exponential.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_modular_exponential(self):\n        self.assertEqual(1, modular_exponential(5, 117, 19))\n        self.assertEqual(\n            pow(1243, 65321, 10**9 + 7), modular_exponential(1243, 65321, 10**9 + 7)\n        )\n        self.assertEqual(1, modular_exponential(12, 0, 78))\n        self.assertRaises(ValueError, modular_exponential, 12, -2, 455)\n\n\nclass TestNextPerfectSquare(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file next_perfect_square.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_find_next_square(self):\n        self.assertEqual(36, find_next_square(25))\n        self.assertEqual(1, find_next_square(0))\n\n    def test_find_next_square2(self):\n        self.assertEqual(36, find_next_square2(25))\n        self.assertEqual(1, find_next_square2(0))\n\n\nclass TestPrimesSieveOfEratosthenes(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file primes_sieve_of_eratosthenes.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_primes(self):\n        self.assertListEqual([2, 3, 5, 7], get_primes(7))\n        self.assertRaises(ValueError, get_primes, -42)\n\n\nclass TestPrimeTest(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file prime_test.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_prime_test(self):\n        \"\"\"\n        checks all prime numbers between 2 up to 100.\n        Between 2 up to 100 exists 25 prime numbers!\n        \"\"\"\n        counter = 0\n        for i in range(2, 101):\n            if prime_check(i):\n                counter += 1\n        self.assertEqual(25, counter)\n\n\nclass TestPythagoras(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file pythagoras.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_pythagoras(self):\n        self.assertEqual(\"Hypotenuse = 3.605551275463989\", pythagoras(3, 2, \"?\"))\n\n\nclass TestRabinMiller(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file rabin_miller.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_prime(self):\n        self.assertTrue(is_prime(7, 2))\n        self.assertTrue(is_prime(13, 11))\n        self.assertFalse(is_prime(6, 2))\n\n\nclass TestRSA(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file rsa.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_encrypt_decrypt(self):\n        self.assertEqual(7, decrypt(encrypt(7, 23, 143), 47, 143))\n\n    # def test_key_generator(self):  # this test takes a while!\n    #     for i in range(100):\n    #         print(\"step {0}\".format(i))\n    #         n, e, d = generate_key(26)\n    #         data = 2\n    #         en = encrypt(data, e, n)\n    #         dec = decrypt(en, d, n)\n    #         self.assertEqual(data,dec)\n\n\nclass TestCombination(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file combination.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_combination(self):\n        self.assertEqual(10, combination(5, 2))\n        self.assertEqual(252, combination(10, 5))\n\n    def test_combination_memo(self):\n        self.assertEqual(10272278170, combination_memo(50, 10))\n        self.assertEqual(847660528, combination_memo(40, 10))\n\n\nclass TestFactorial(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file factorial.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_factorial(self):\n        self.assertEqual(1, factorial(0))\n        self.assertEqual(120, factorial(5))\n        self.assertEqual(3628800, factorial(10))\n        self.assertEqual(637816310, factorial(34521, 10**9 + 7))\n        self.assertRaises(ValueError, factorial, -42)\n        self.assertRaises(ValueError, factorial, 42, -1)\n\n    def test_factorial_recur(self):\n        self.assertEqual(1, factorial_recur(0))\n        self.assertEqual(120, factorial_recur(5))\n        self.assertEqual(3628800, factorial_recur(10))\n        self.assertEqual(637816310, factorial_recur(34521, 10**9 + 7))\n        self.assertRaises(ValueError, factorial_recur, -42)\n        self.assertRaises(ValueError, factorial_recur, 42, -1)\n\n\nclass TestHailstone(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file hailstone.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_hailstone(self):\n        self.assertEqual([8, 4, 2, 1], hailstone.hailstone(8))\n        self.assertEqual([10, 5, 16, 8, 4, 2, 1], hailstone.hailstone(10))\n\n\nclass TestCosineSimilarity(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file cosine_similarity.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_cosine_similarity(self):\n        vec_a = [1, 1, 1]\n        vec_b = [-1, -1, -1]\n        vec_c = [1, 2, -1]\n        self.assertAlmostEqual(cosine_similarity(vec_a, vec_a), 1)\n        self.assertAlmostEqual(cosine_similarity(vec_a, vec_b), -1)\n        self.assertAlmostEqual(cosine_similarity(vec_a, vec_c), 0.4714045208)\n\n\nclass TestFindPrimitiveRoot(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file find_primitive_root_simple.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_find_primitive_root_simple(self):\n        self.assertListEqual([0], find_primitive_root(1))\n        self.assertListEqual([2, 3], find_primitive_root(5))\n        self.assertListEqual([], find_primitive_root(24))\n        self.assertListEqual(\n            [2, 5, 13, 15, 17, 18, 19, 20, 22, 24, 32, 35], find_primitive_root(37)\n        )\n\n\nclass TestFindOrder(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file find_order_simple.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_find_order_simple(self):\n        self.assertEqual(1, find_order(1, 1))\n        self.assertEqual(6, find_order(3, 7))\n        self.assertEqual(-1, find_order(128, 256))\n        self.assertEqual(352, find_order(3, 353))\n\n\nclass TestKrishnamurthyNumber(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file krishnamurthy_number.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_krishnamurthy_number(self):\n        self.assertFalse(krishnamurthy_number(0))\n        self.assertTrue(krishnamurthy_number(2))\n        self.assertTrue(krishnamurthy_number(1))\n        self.assertTrue(krishnamurthy_number(145))\n        self.assertTrue(krishnamurthy_number(40585))\n\n\nclass TestMagicNumber(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file find_order_simple.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_magic_number(self):\n        self.assertTrue(magic_number(50113))\n        self.assertTrue(magic_number(1234))\n        self.assertTrue(magic_number(100))\n        self.assertTrue(magic_number(199))\n        self.assertFalse(magic_number(2000))\n        self.assertFalse(magic_number(500000))\n\n\nclass TestDiffieHellmanKeyExchange(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file diffie_hellman_key_exchange.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_find_order_simple(self):\n        self.assertFalse(diffie_hellman_key_exchange(3, 6))\n        self.assertTrue(diffie_hellman_key_exchange(3, 353))\n        self.assertFalse(diffie_hellman_key_exchange(5, 211))\n        self.assertTrue(diffie_hellman_key_exchange(11, 971))\n\n\nclass TestNumberOfDigits(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file num_digits.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_num_digits(self):\n        self.assertEqual(2, num_digits(12))\n        self.assertEqual(5, num_digits(99999))\n        self.assertEqual(1, num_digits(8))\n        self.assertEqual(1, num_digits(0))\n        self.assertEqual(1, num_digits(-5))\n        self.assertEqual(3, num_digits(-254))\n\n\nclass TestNumberOfPerfectSquares(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file num_perfect_squares.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_num_perfect_squares(self):\n        self.assertEqual(4, num_perfect_squares(31))\n        self.assertEqual(3, num_perfect_squares(12))\n        self.assertEqual(2, num_perfect_squares(13))\n        self.assertEqual(2, num_perfect_squares(10))\n        self.assertEqual(4, num_perfect_squares(1500))\n        self.assertEqual(2, num_perfect_squares(1548524521))\n        self.assertEqual(3, num_perfect_squares(9999999993))\n        self.assertEqual(1, num_perfect_squares(9))\n\n\nclass TestChineseRemainderSolver(unittest.TestCase):\n    def test_k_three(self):\n        # Example which should give the answer 143\n        # which is the smallest possible x that\n        # solves the system of equations\n        num = [3, 7, 10]\n        rem = [2, 3, 3]\n        self.assertEqual(\n            chinese_remainder_theorem.solve_chinese_remainder(num, rem), 143\n        )\n\n    def test_k_five(self):\n        # Example which should give the answer 3383\n        # which is the smallest possible x that\n        # solves the system of equations\n        num = [3, 5, 7, 11, 26]\n        rem = [2, 3, 2, 6, 3]\n        self.assertEqual(\n            chinese_remainder_theorem.solve_chinese_remainder(num, rem), 3383\n        )\n\n    def test_exception_non_coprime(self):\n        # There should be an exception when all\n        # numbers in num are not pairwise coprime\n        num = [3, 7, 10, 14]\n        rem = [2, 3, 3, 1]\n        with self.assertRaises(Exception):  # noqa: B017\n            chinese_remainder_theorem.solve_chinese_remainder(num, rem)\n\n    def test_empty_lists(self):\n        num = []\n        rem = []\n        with self.assertRaises(Exception):  # noqa: B017\n            chinese_remainder_theorem.solve_chinese_remainder(num, rem)\n\n\nclass TestFFT(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file fft.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_real_numbers(self):\n        x = [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]\n        y = [4.000, 2.613, 0.000, 1.082, 0.000, 1.082, 0.000, 2.613]\n        # abs(complex) returns the magnitude\n        result = [float(f\"{abs(f):.3f}\") for f in fft.fft(x)]\n        self.assertEqual(result, y)\n\n    def test_all_zero(self):\n        x = [0.0, 0.0, 0.0, 0.0]\n        y = [0.0, 0.0, 0.0, 0.0]\n        result = [float(f\"{abs(f):.1f}\") for f in fft.fft(x)]\n        self.assertEqual(result, y)\n\n    def test_all_ones(self):\n        x = [1.0, 1.0, 1.0, 1.0]\n        y = [4.0, 0.0, 0.0, 0.0]\n        result = [float(f\"{abs(f):.1f}\") for f in fft.fft(x)]\n        self.assertEqual(result, y)\n\n    def test_complex_numbers(self):\n        x = [2.0 + 2j, 1.0 + 3j, 3.0 + 1j, 2.0 + 2j]\n        real = [8.0, 0.0, 2.0, -2.0]\n        imag = [8.0, 2.0, -2.0, 0.0]\n        real_result = [float(f\"{f.real:.1f}\") for f in fft.fft(x)]\n        imag_result = [float(f\"{f.imag:.1f}\") for f in fft.fft(x)]\n        self.assertEqual(real, real_result)\n        self.assertEqual(imag, imag_result)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_matrix.py",
    "content": "import unittest\n\nfrom algorithms.matrix import (\n    bomb_enemy,\n    cholesky_matrix_decomposition,\n    copy_transform,\n    crout_matrix_decomposition,\n    matrix_exponentiation,\n    matrix_inversion,\n    multiply,\n    rotate_image,\n    sort_matrix_diagonally,\n    sparse_dot_vector,\n    spiral_traversal,\n    sudoku_validator,\n    sum_sub_squares,\n)\n\n\nclass TestBombEnemy(unittest.TestCase):\n    def test_3x4(self):\n        grid1 = [[\"0\", \"E\", \"0\", \"0\"], [\"E\", \"0\", \"W\", \"E\"], [\"0\", \"E\", \"0\", \"0\"]]\n        self.assertEqual(3, bomb_enemy.max_killed_enemies(grid1))\n\n        grid1 = [\n            [\"0\", \"E\", \"0\", \"E\"],\n            [\"E\", \"E\", \"E\", \"0\"],\n            [\"E\", \"0\", \"W\", \"E\"],\n            [\"0\", \"E\", \"0\", \"0\"],\n        ]\n        grid2 = [\n            [\"0\", \"0\", \"0\", \"E\"],\n            [\"E\", \"0\", \"0\", \"0\"],\n            [\"E\", \"0\", \"W\", \"E\"],\n            [\"0\", \"E\", \"0\", \"0\"],\n        ]\n        self.assertEqual(5, bomb_enemy.max_killed_enemies(grid1))\n        self.assertEqual(3, bomb_enemy.max_killed_enemies(grid2))\n\n\nclass TestCopyTransform(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file copy_transform.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_copy_transform(self):\n        self.assertEqual(\n            copy_transform.rotate_clockwise([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [[7, 4, 1], [8, 5, 2], [9, 6, 3]],\n        )\n\n        self.assertEqual(\n            copy_transform.rotate_counterclockwise([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [[3, 6, 9], [2, 5, 8], [1, 4, 7]],\n        )\n\n        self.assertEqual(\n            copy_transform.top_left_invert([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [[1, 4, 7], [2, 5, 8], [3, 6, 9]],\n        )\n\n        self.assertEqual(\n            copy_transform.bottom_left_invert([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [[9, 6, 3], [8, 5, 2], [7, 4, 1]],\n        )\n\n\nclass TestCroutMatrixDecomposition(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file crout_matrix_decomposition.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_crout_matrix_decomposition(self):\n        self.assertEqual(\n            ([[9.0, 0.0], [7.0, 0.0]], [[1.0, 1.0], [0.0, 1.0]]),\n            crout_matrix_decomposition.crout_matrix_decomposition([[9, 9], [7, 7]]),\n        )\n\n        self.assertEqual(\n            (\n                [[1.0, 0.0, 0.0], [3.0, -2.0, 0.0], [6.0, -5.0, 0.0]],\n                [[1.0, 2.0, 3.0], [0.0, 1.0, 2.0], [0.0, 0.0, 1.0]],\n            ),\n            crout_matrix_decomposition.crout_matrix_decomposition(\n                [[1, 2, 3], [3, 4, 5], [6, 7, 8]]\n            ),\n        )\n\n        self.assertEqual(\n            (\n                [\n                    [2.0, 0, 0, 0],\n                    [4.0, -1.0, 0, 0],\n                    [6.0, -2.0, 2.0, 0],\n                    [8.0, -3.0, 3.0, 0.0],\n                ],\n                [\n                    [1.0, 0.5, 1.5, 0.5],\n                    [0, 1.0, 2.0, 1.0],\n                    [0, 0, 1.0, 0.0],\n                    [0, 0, 0, 1.0],\n                ],\n            ),\n            crout_matrix_decomposition.crout_matrix_decomposition(\n                [[2, 1, 3, 1], [4, 1, 4, 1], [6, 1, 7, 1], [8, 1, 9, 1]]\n            ),\n        )\n\n\nclass TestCholeskyMatrixDecomposition(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file cholesky_matrix_decomposition.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_cholesky_matrix_decomposition(self):\n        self.assertEqual(\n            [[2.0, 0.0, 0.0], [6.0, 1.0, 0.0], [-8.0, 5.0, 3.0]],\n            cholesky_matrix_decomposition.cholesky_decomposition(\n                [[4, 12, -16], [12, 37, -43], [-16, -43, 98]]\n            ),\n        )\n\n        self.assertEqual(\n            None,\n            cholesky_matrix_decomposition.cholesky_decomposition(\n                [[4, 12, -8], [12, 4, -43], [-16, -1, 32]]\n            ),\n        )\n\n        self.assertEqual(\n            None,\n            cholesky_matrix_decomposition.cholesky_decomposition(\n                [[4, 12, -16], [12, 37, -43], [-16, -43, 98], [1, 2, 3]]\n            ),\n        )\n\n        # example taken from https://ece.uwaterloo.ca/~dwharder/NumericalAnalysis/04LinearAlgebra/cholesky/\n        self.assertEqual(\n            [\n                [2.23606797749979, 0.0, 0.0, 0.0],\n                [0.5366563145999494, 2.389979079406345, 0.0, 0.0],\n                [0.13416407864998736, -0.19749126846635062, 2.818332343581848, 0.0],\n                [\n                    -0.2683281572999747,\n                    0.43682390737048743,\n                    0.64657701271919,\n                    3.052723872310221,\n                ],\n            ],\n            cholesky_matrix_decomposition.cholesky_decomposition(\n                [\n                    [5, 1.2, 0.3, -0.6],\n                    [1.2, 6, -0.4, 0.9],\n                    [0.3, -0.4, 8, 1.7],\n                    [-0.6, 0.9, 1.7, 10],\n                ]\n            ),\n        )\n\n\nclass TestInversion(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file matrix_inversion.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_inversion(self):\n        from fractions import Fraction\n\n        m1 = [[1, 1], [1, 2]]\n        self.assertEqual(matrix_inversion.invert_matrix(m1), [[2, -1], [-1, 1]])\n\n        m2 = [[1, 2], [3, 4, 5]]\n        self.assertEqual(matrix_inversion.invert_matrix(m2), [[-1]])\n\n        m3 = [[1, 1, 1, 1], [2, 2, 2, 2]]\n        self.assertEqual(matrix_inversion.invert_matrix(m3), [[-2]])\n\n        m4 = [[1]]\n        self.assertEqual(matrix_inversion.invert_matrix(m4), [[-3]])\n\n        m5 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n        self.assertEqual(matrix_inversion.invert_matrix(m5), [[-4]])\n\n        m6 = [[3, 5, 1], [2, 5, 0], [1, 9, 8]]\n        self.assertEqual(\n            matrix_inversion.invert_matrix(m6),\n            [\n                [Fraction(40, 53), Fraction(-31, 53), Fraction(-5, 53)],\n                [Fraction(-16, 53), Fraction(23, 53), Fraction(2, 53)],\n                [Fraction(13, 53), Fraction(-22, 53), Fraction(5, 53)],\n            ],\n        )\n\n\nclass TestMatrixExponentiation(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file matrix_exponentiation.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_matrix_exponentiation(self):\n        mat = [[1, 0, 2], [2, 1, 0], [0, 2, 1]]\n\n        self.assertEqual(\n            matrix_exponentiation.matrix_exponentiation(mat, 0),\n            [[1, 0, 0], [0, 1, 0], [0, 0, 1]],\n        )\n\n        self.assertEqual(\n            matrix_exponentiation.matrix_exponentiation(mat, 1),\n            [[1, 0, 2], [2, 1, 0], [0, 2, 1]],\n        )\n\n        self.assertEqual(\n            matrix_exponentiation.matrix_exponentiation(mat, 2),\n            [[1, 4, 4], [4, 1, 4], [4, 4, 1]],\n        )\n\n        self.assertEqual(\n            matrix_exponentiation.matrix_exponentiation(mat, 5),\n            [[81, 72, 90], [90, 81, 72], [72, 90, 81]],\n        )\n\n\nclass TestMultiply(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file multiply.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_multiply(self):\n        self.assertEqual(\n            multiply.multiply([[1, 2, 3], [2, 1, 1]], [[1], [2], [3]]), [[14], [7]]\n        )\n\n\nclass TestRotateImage(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file rotate_image.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_rotate_image(self):\n        self.assertEqual(\n            rotate_image.rotate([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [[7, 4, 1], [8, 5, 2], [9, 6, 3]],\n        )\n\n\nclass TestSparseDotVector(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file sparse_dot_vector.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_sparse_dot_vector(self):\n        self.assertEqual(\n            sparse_dot_vector.dot_product(\n                sparse_dot_vector.vector_to_index_value_list([1.0, 2.0, 3.0]),\n                sparse_dot_vector.vector_to_index_value_list([0.0, 2.0, 2.0]),\n            ),\n            10,\n        )\n\n\nclass TestSpiralTraversal(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file spiral_traversal.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_spiral_traversal(self):\n        self.assertEqual(\n            spiral_traversal.spiral_traversal([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            [1, 2, 3, 6, 9, 8, 7, 4, 5],\n        )\n\n\nclass TestSudokuValidator(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file sudoku_validator.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_sudoku_validator(self):\n        self.assertTrue(\n            sudoku_validator.valid_solution(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 5, 3, 4, 8],\n                    [1, 9, 8, 3, 4, 2, 5, 6, 7],\n                    [8, 5, 9, 7, 6, 1, 4, 2, 3],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 6, 1, 5, 3, 7, 2, 8, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 4, 5, 2, 8, 6, 1, 7, 9],\n                ]\n            )\n        )\n\n        self.assertTrue(\n            sudoku_validator.valid_solution_hashtable(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 5, 3, 4, 8],\n                    [1, 9, 8, 3, 4, 2, 5, 6, 7],\n                    [8, 5, 9, 7, 6, 1, 4, 2, 3],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 6, 1, 5, 3, 7, 2, 8, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 4, 5, 2, 8, 6, 1, 7, 9],\n                ]\n            )\n        )\n\n        self.assertTrue(\n            sudoku_validator.valid_solution_set(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 5, 3, 4, 8],\n                    [1, 9, 8, 3, 4, 2, 5, 6, 7],\n                    [8, 5, 9, 7, 6, 1, 4, 2, 3],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 6, 1, 5, 3, 7, 2, 8, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 4, 5, 2, 8, 6, 1, 7, 9],\n                ]\n            )\n        )\n\n        self.assertFalse(\n            sudoku_validator.valid_solution(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 0, 3, 4, 9],\n                    [1, 0, 0, 3, 4, 2, 5, 6, 0],\n                    [8, 5, 9, 7, 6, 1, 0, 2, 0],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 0, 1, 5, 3, 7, 2, 1, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 0, 0, 4, 8, 1, 1, 7, 9],\n                ]\n            )\n        )\n\n        self.assertFalse(\n            sudoku_validator.valid_solution_hashtable(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 0, 3, 4, 9],\n                    [1, 0, 0, 3, 4, 2, 5, 6, 0],\n                    [8, 5, 9, 7, 6, 1, 0, 2, 0],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 0, 1, 5, 3, 7, 2, 1, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 0, 0, 4, 8, 1, 1, 7, 9],\n                ]\n            )\n        )\n\n        self.assertFalse(\n            sudoku_validator.valid_solution_set(\n                [\n                    [5, 3, 4, 6, 7, 8, 9, 1, 2],\n                    [6, 7, 2, 1, 9, 0, 3, 4, 9],\n                    [1, 0, 0, 3, 4, 2, 5, 6, 0],\n                    [8, 5, 9, 7, 6, 1, 0, 2, 0],\n                    [4, 2, 6, 8, 5, 3, 7, 9, 1],\n                    [7, 1, 3, 9, 2, 4, 8, 5, 6],\n                    [9, 0, 1, 5, 3, 7, 2, 1, 4],\n                    [2, 8, 7, 4, 1, 9, 6, 3, 5],\n                    [3, 0, 0, 4, 8, 1, 1, 7, 9],\n                ]\n            )\n        )\n\n\nclass TestSumSubSquares(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file sum_sub_squares.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_sum_sub_squares(self):\n        mat = [\n            [1, 1, 1, 1, 1],\n            [2, 2, 2, 2, 2],\n            [3, 3, 3, 3, 3],\n            [4, 4, 4, 4, 4],\n            [5, 5, 5, 5, 5],\n        ]\n        self.assertEqual(\n            sum_sub_squares.sum_sub_squares(mat, 3),\n            [[18, 18, 18], [27, 27, 27], [36, 36, 36]],\n        )\n\n\nclass TestSortMatrixDiagonally(unittest.TestCase):\n    def test_sort_diagonally(self):\n        mat = [[3, 3, 1, 1], [2, 2, 1, 2], [1, 1, 1, 2]]\n\n        self.assertEqual(\n            sort_matrix_diagonally.sort_diagonally(mat),\n            [[1, 1, 1, 1], [1, 2, 2, 2], [1, 2, 3, 3]],\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_monomial.py",
    "content": "import math\nimport unittest\nfrom fractions import Fraction\n\nfrom algorithms.math.polynomial import Monomial\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.m1 = Monomial({})\n        self.m2 = Monomial({1: 1}, 2)\n        self.m3 = Monomial({1: 2, 2: -1}, 1.5)\n        self.m4 = Monomial({1: 1, 2: 2, 3: -2}, 3)\n        self.m5 = Monomial({2: 1, 3: 0}, Fraction(2, 3))\n        self.m6 = Monomial({1: 0, 2: 0, 3: 0}, -2.27)\n        self.m7 = Monomial({1: 2, 7: 2}, -math.pi)\n        self.m8 = Monomial({150: 5, 170: 2, 10000: 3}, 0)\n        self.m9 = 2\n        self.m10 = math.pi\n        self.m11 = Fraction(3, 8)\n        self.m12 = 0\n        self.m13 = Monomial({1: 1}, -2)\n        self.m14 = Monomial({1: 2}, 3)\n        self.m15 = Monomial({1: 1}, 3)\n        self.m16 = Monomial({1: 2, 7: 2}, math.pi)\n        self.m17 = Monomial({1: -1})\n\n    def test_monomial_addition(self):\n\n        # Monomials with different underlying variables or\n        # even different power of those variables must not be added!\n        self.assertRaises(ValueError, lambda x, y: x + y, self.m1, self.m2)\n        self.assertRaises(ValueError, lambda x, y: x + y, self.m2, self.m3)\n        self.assertRaises(ValueError, lambda x, y: x + y, self.m2, self.m14)\n\n        # Additive inverses of each other should produce the zero monomial.\n        self.assertEqual(self.m13 + self.m2, self.m1)\n\n        # Zero monomial + Zero monomial = Zero monomial\n        self.assertEqual(self.m1 + self.m1, self.m1)\n\n        # Coefficient float.\n        self.assertEqual(self.m7 + self.m7, Monomial({1: 2, 7: 2}, -2 * math.pi))\n\n        # Coefficient 0 so should equal the zero monomial.\n        self.assertEqual(self.m8, self.m1)\n\n        # The constant term cannot be added to any monomial\n        # that has any variables.\n        self.assertRaises(ValueError, lambda x, y: x + y, self.m2, self.m9)\n\n        # Any literal cannot be added to a Monomial. However, a monomial\n        # can be added to any int, float, Fraction, or Monomial.\n\n        # So 2 + Monomial is raises TypeError but Monomial + 2 may work fine!\n        self.assertRaises(TypeError, lambda x, y: x + y, self.m9, self.m2)\n\n        # Any constant added to a zero monomial produces\n        # a monomial.\n        self.assertEqual(self.m1 + self.m9, Monomial({}, 2))\n        self.assertEqual(self.m1 + self.m12, Monomial({}, 0))\n\n        return\n\n    def test_monomial_subtraction(self):\n\n        # Monomials with different underlying variables or\n        # even different power of those variables must not be subtracted!\n        self.assertRaises(ValueError, lambda x, y: x - y, self.m1, self.m2)\n        self.assertRaises(ValueError, lambda x, y: x - y, self.m2, self.m3)\n        self.assertRaises(ValueError, lambda x, y: x - y, self.m2, self.m14)\n\n        # Additive inverses of each other should produce the zero monomial.\n        self.assertEqual(self.m2 - self.m2, self.m1)\n        self.assertEqual(self.m2 - self.m2, Monomial({}, 0))\n\n        # Zero monomial - Zero monomial = Zero monomial\n        self.assertEqual(self.m1 - self.m1, self.m1)\n\n        # Coefficient int.\n        self.assertEqual(self.m2 - self.m15, Monomial({1: 1}, -1))\n\n        # Coefficient float.\n        self.assertEqual(self.m16 - self.m7, Monomial({1: 2, 7: 2}, 2 * math.pi))\n\n        # The constant term cannot be added to any monomial\n        # that has any variables.\n        self.assertRaises(ValueError, lambda x, y: x - y, self.m2, self.m9)\n\n        # Any literal cannot be added to a Monomial. However, a monomial\n        # can be added to any int, float, Fraction, or Monomial.\n\n        # So 2 + Monomial is raises TypeError but Monomial + 2 may work fine!\n        self.assertRaises(TypeError, lambda x, y: x - y, self.m9, self.m2)\n\n        # Any constant added to a zero monomial produces\n        # a monomial.\n        self.assertEqual(self.m1 - self.m9, Monomial({}, -2))\n        self.assertEqual(self.m1 - self.m12, Monomial({}, 0))\n\n        return\n\n    def test_monomial_multiplication(self):\n\n        # Usual multiplication.\n        # The positive and negative powers of the same variable\n        # should cancel out.\n        self.assertEqual(self.m2 * self.m13, Monomial({1: 2}, -4))\n        self.assertEqual(self.m2 * self.m17, Monomial({}, 2))\n\n        # A coefficient of zero should make the product zero.\n        # Zero monomial * any int, float, Fraction, or Monomial = Zero monomial\n        self.assertEqual(self.m8 * self.m5, self.m1)\n        self.assertEqual(self.m1 * self.m2, self.m1)\n\n        # Test usual float multiplication.\n        self.assertEqual(\n            self.m7 * self.m3, Monomial({1: 4, 2: -1, 7: 2}, -1.5 * math.pi)\n        )\n\n        return\n\n    def test_monomial_inverse(self):\n\n        # The Zero monomial is not invertible.\n        self.assertRaises(ValueError, lambda x: x.inverse(), self.m1)\n        self.assertRaises(ValueError, lambda x: x.inverse(), self.m8)\n        self.assertRaises(ValueError, lambda x: x.inverse(), Monomial({}, self.m12))\n\n        # Check some inverses.\n        self.assertEqual(self.m7.inverse(), Monomial({1: -2, 7: -2}, -1 / math.pi))\n\n        # Doesn't matter if the coefficient is Fraction or float.\n        # Both should be treated as same.\n        self.assertEqual(self.m5.inverse(), Monomial({2: -1}, Fraction(3, 2)))\n        self.assertEqual(self.m5.inverse(), Monomial({2: -1}, 1.5))\n\n        # Should work fine without variables too!\n        self.assertTrue(self.m6.inverse(), Monomial({}, Fraction(-100, 227)))\n        self.assertEqual(self.m6.inverse(), Monomial({}, -1 / 2.27))\n        return\n\n    def test_monomial_division(self):\n        # Any monomial divided by the Zero Monomial should raise a ValueError.\n        self.assertRaises(ValueError, lambda x, y: x.__truediv__(y), self.m2, self.m1)\n        self.assertRaises(ValueError, lambda x, y: x.__truediv__(y), self.m2, self.m8)\n        self.assertRaises(ValueError, lambda x, y: x.__truediv__(y), self.m2, self.m12)\n\n        # Test some usual cases.\n        self.assertEqual(self.m7 / self.m3, Monomial({2: 1, 7: 2}, -2 * math.pi / 3))\n        self.assertEqual(self.m14 / self.m13, Monomial({1: 1}) * Fraction(-3, 2))\n\n        return\n\n    def test_monomial_substitution(self):\n        # Test with int.\n        self.assertAlmostEqual(self.m7.substitute(2), -16 * math.pi, delta=1e-9)\n        # Test with float.\n        self.assertAlmostEqual(self.m7.substitute(1.5), (1.5**4) * -math.pi, delta=1e-9)\n        # Test with Fraction.\n        self.assertAlmostEqual(\n            self.m7.substitute(Fraction(-1, 2)),\n            (Fraction(-1, 2) ** 4) * -math.pi,\n            delta=1e-9,\n        )\n        # Test with a complete substitution map.\n        self.assertAlmostEqual(\n            self.m7.substitute({1: 3, 7: 0}), (3**2) * (0**2) * -math.pi, delta=1e-9\n        )\n        # Test with a more than complete substitution map.\n        self.assertAlmostEqual(\n            self.m7.substitute({1: 3, 7: 0, 2: 2}),\n            (3**2) * (0**2) * -math.pi,\n            delta=1e-9,\n        )\n\n        # Should raise a ValueError if not enough variables are supplied!\n        self.assertRaises(\n            ValueError, lambda x, y: x.substitute(y), self.m7, {1: 3, 2: 2}\n        )\n        self.assertRaises(ValueError, lambda x, y: x.substitute(y), self.m7, {2: 2})\n\n        # The zero monomial always gives zero upon substitution.\n        self.assertEqual(self.m8.substitute(2), 0)\n        self.assertEqual(self.m8.substitute({1231: 2, 1: 2}), 0)\n\n        return\n\n    def test_monomial_all_variables(self):\n\n        # Any variable with zero power should not exist in the set\n        # of variables.\n        self.assertEqual(self.m5.all_variables(), {2})\n        self.assertEqual(self.m6.all_variables(), set())\n\n        # The zero monomial should output empty set.\n        self.assertEqual(self.m8.all_variables(), set())\n\n        return\n\n    def test_monomial_clone(self):\n\n        # A monomial should produce its copy\n        # with same underlying variable dictionary\n        # and same coefficient.\n        self.assertEqual(self.m3, self.m3.clone())\n\n        # The zero monomial is identified and\n        # always clones to itself.\n        self.assertEqual(self.m1, self.m8.clone())\n        self.assertEqual(self.m1, self.m1.clone())\n        self.assertEqual(self.m8, self.m1.clone())\n        self.assertEqual(self.m8, self.m8.clone())\n        return\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_polynomial.py",
    "content": "import math\nimport unittest\nfrom fractions import Fraction\n\nfrom algorithms.math.polynomial import Monomial, Polynomial\n\n\nclass TestSuite(unittest.TestCase):\n    def setUp(self):\n        self.p0 = Polynomial([Monomial({})])\n        self.p1 = Polynomial([Monomial({}), Monomial({})])\n        self.p2 = Polynomial([Monomial({1: 1}, 2)])\n        self.p3 = Polynomial([Monomial({1: 1}, 2), Monomial({1: 2, 2: -1}, 1.5)])\n        self.p4 = Polynomial(\n            [\n                Monomial({2: 1, 3: 0}, Fraction(2, 3)),\n                Monomial({1: -1, 3: 2}, math.pi),\n                Monomial({1: -1, 3: 2}, 1),\n            ]\n        )\n        self.p5 = Polynomial(\n            [\n                Monomial({150: 5, 170: 2, 10000: 3}, 0),\n                Monomial({1: -1, 3: 2}, 1),\n            ]\n        )\n        self.p6 = Polynomial(\n            [2, -3, Fraction(1, 7), 2**math.pi, Monomial({2: 3, 3: 1}, 1.25)]\n        )\n        self.p7 = Polynomial([Monomial({1: 1}, -2), Monomial({1: 2, 2: -1}, -1.5)])\n\n        self.m1 = Monomial({1: 2, 2: 3}, -1)\n\n        return\n\n    def test_polynomial_addition(self):\n\n        # The zero polynomials should add up to\n        # itselves only.\n        self.assertEqual(self.p0 + self.p1, self.p0)\n        self.assertEqual(self.p0 + self.p1, self.p1)\n\n        # Additive inverses should add up to the\n        # zero polynomial.\n        self.assertEqual(self.p3 + self.p7, self.p0)\n        self.assertEqual(self.p3 + self.p7, self.p1)\n\n        # Like terms should combine.\n        # The order of monomials should not matter.\n        self.assertEqual(\n            self.p2 + self.p3,\n            Polynomial([Monomial({1: 1}, 4), Monomial({1: 2, 2: -1}, 1.5)]),\n        )\n        self.assertEqual(\n            self.p2 + self.p3,\n            Polynomial(\n                [\n                    Monomial({1: 2, 2: -1}, 1.5),\n                    Monomial({1: 1}, 4),\n                ]\n            ),\n        )\n\n        # Another typical computation.\n        self.assertEqual(\n            self.p5 + self.p6,\n            Polynomial(\n                [\n                    Monomial({}, 7.96783496993343),\n                    Monomial({2: 3, 3: 1}, 1.25),\n                    Monomial({1: -1, 3: 2}),\n                ]\n            ),\n        )\n\n        return\n\n    def test_polynomial_subtraction(self):\n\n        self.assertEqual(self.p3 - self.p2, Polynomial([Monomial({1: 2, 2: -1}, 1.5)]))\n\n        self.assertEqual(self.p3 - self.p3, Polynomial([]))\n\n        self.assertEqual(self.p2 - self.p3, Polynomial([Monomial({1: 2, 2: -1}, -1.5)]))\n\n        pass\n\n    def test_polynomial_multiplication(self):\n        self.assertEqual(self.p0 * self.p2, Polynomial([]))\n        self.assertEqual(self.p1 * self.p2, Polynomial([]))\n\n        self.assertEqual(\n            self.p2 * self.p3,\n            Polynomial([Monomial({1: 2}, 4), Monomial({1: 3, 2: -1}, Fraction(3, 1))]),\n        )\n        return\n\n    def test_polynomial_variables(self):\n        # The zero polynomial has no variables.\n\n        self.assertEqual(self.p0.variables(), set())\n        self.assertEqual(self.p1.variables(), set())\n\n        # The total variables are the union of the variables\n        # from the monomials.\n        self.assertEqual(self.p4.variables(), {1, 2, 3})\n\n        # The monomials with coefficient 0 should be dropped.\n        self.assertEqual(self.p5.variables(), {1, 3})\n        return\n\n    def test_polynomial_subs(self):\n        # Anything substitued in the zero polynomial\n        # should evaluate to 0.\n        self.assertEqual(self.p1.subs(2), 0)\n        self.assertEqual(self.p0.subs(-101231), 0)\n\n        # Should raise a ValueError if not enough variables are supplied.\n        self.assertRaises(ValueError, lambda x, y: x.subs(y), self.p4, {1: 3, 2: 2})\n        self.assertRaises(ValueError, lambda x, y: x.subs(y), self.p4, {})\n\n        # Should work fine if a complete subsitution map is provided.\n        self.assertAlmostEqual(\n            self.p4.subs({1: 1, 2: 1, 3: 1}), (1 + math.pi + Fraction(2, 3)), delta=1e-9\n        )\n        # Should work fine if more than enough substitutions are provided.\n        self.assertAlmostEqual(\n            self.p4.subs({1: 1, 2: 1, 3: 1, 4: 1}),\n            (1 + math.pi + Fraction(2, 3)),\n            delta=1e-9,\n        )\n        return\n\n    def test_polynomial_clone(self):\n\n        # The zero polynomial always clones to itself.\n        self.assertEqual(self.p0.clone(), self.p0)\n        self.assertEqual(self.p1.clone(), self.p0)\n        self.assertEqual(self.p0.clone(), self.p1)\n        self.assertEqual(self.p1.clone(), self.p1)\n\n        # The polynomial should clone nicely.\n        self.assertEqual(self.p4.clone(), self.p4)\n\n        # The monomial with a zero coefficient should be dropped\n        # in the clone.\n        self.assertEqual(self.p5.clone(), Polynomial([Monomial({1: -1, 3: 2}, 1)]))\n        return\n\n    def test_polynomial_long_division(self):\n        \"\"\"\n        Test polynomial long division\n        \"\"\"\n\n        # Dividend: 4a_1^3 + 3a_1^2 - 2a_1 + 5\n        dividend = Polynomial(\n            [\n                Monomial({1: 3}, 4),  # 4(a_1)^3\n                Monomial({1: 2}, 3),  # 3(a_1)^2\n                Monomial({1: 1}, -2),  # -2(a_1)\n                Monomial({}, 5),  # +5\n            ]\n        )\n\n        # Divisor: 2a_1 - 1\n        divisor = Polynomial(\n            [\n                Monomial({1: 1}, 2),  # 2(a_1)\n                Monomial({}, -1),  # -1\n            ]\n        )\n\n        # Expected Quotient: 2a_1^2 + (5/2)a_1 + 1/4\n        expected_quotient = Polynomial(\n            [\n                Monomial({1: 2}, 2),  # 2(a_1)^2\n                Monomial({1: 1}, Fraction(5, 2)),  # (5/2)(a_1)\n                Monomial({}, Fraction(1, 4)),  # +1/4\n            ]\n        )\n\n        # Expected Remainder: 21/4\n        expected_remainder = Polynomial(\n            [\n                Monomial({}, Fraction(21, 4))  # 21/4\n            ]\n        )\n\n        quotient_long_div, remainder_long_div = dividend.poly_long_division(divisor)\n\n        quotient_truediv = (\n            dividend / divisor\n        )  # Calls __truediv__, which returns only the quotient\n\n        # Check if quotient from poly_long_division matches expected\n        self.assertEqual(quotient_long_div, expected_quotient)\n\n        # Check if remainder from poly_long_division matches expected\n        self.assertEqual(remainder_long_div, expected_remainder)\n\n        # Check if quotient from __truediv__ matches quotient from poly_long_division\n        self.assertEqual(quotient_truediv, quotient_long_div)\n\n        return\n"
  },
  {
    "path": "tests/test_queue.py",
    "content": "import unittest\n\nfrom algorithms.queue import (\n    ArrayQueue,\n    LinkedListQueue,\n    PriorityQueue,\n    max_sliding_window,\n    reconstruct_queue,\n)\n\n\nclass TestQueue(unittest.TestCase):\n    \"\"\"\n    Test suite for the Queue data structures.\n    \"\"\"\n\n    def test_array_queue(self):\n        queue = ArrayQueue()\n        queue.enqueue(1)\n        queue.enqueue(2)\n        queue.enqueue(3)\n\n        # test __iter__()\n        it = iter(queue)\n        self.assertEqual(1, next(it))\n        self.assertEqual(2, next(it))\n        self.assertEqual(3, next(it))\n        self.assertRaises(StopIteration, next, it)\n\n        # test __len__()\n        self.assertEqual(3, len(queue))\n\n        # test is_empty()\n        self.assertFalse(queue.is_empty())\n\n        # test peek()\n        self.assertEqual(1, queue.peek())\n\n        # test dequeue()\n        self.assertEqual(1, queue.dequeue())\n        self.assertEqual(2, queue.dequeue())\n        self.assertEqual(3, queue.dequeue())\n\n        self.assertTrue(queue.is_empty())\n\n    def test_linked_list_queue(self):\n        queue = LinkedListQueue()\n        queue.enqueue(1)\n        queue.enqueue(2)\n        queue.enqueue(3)\n\n        # test __iter__()\n        it = iter(queue)\n        self.assertEqual(1, next(it))\n        self.assertEqual(2, next(it))\n        self.assertEqual(3, next(it))\n        self.assertRaises(StopIteration, next, it)\n\n        # test __len__()\n        self.assertEqual(3, len(queue))\n\n        # test is_empty()\n        self.assertFalse(queue.is_empty())\n\n        # test peek()\n        self.assertEqual(1, queue.peek())\n\n        # test dequeue()\n        self.assertEqual(1, queue.dequeue())\n        self.assertEqual(2, queue.dequeue())\n        self.assertEqual(3, queue.dequeue())\n\n        self.assertTrue(queue.is_empty())\n\n\nclass TestSuite(unittest.TestCase):\n    def test_max_sliding_window(self):\n        array = [1, 3, -1, -3, 5, 3, 6, 7]\n        self.assertEqual(max_sliding_window(array, k=5), [5, 5, 6, 7])\n        self.assertEqual(max_sliding_window(array, k=3), [3, 3, 5, 5, 6, 7])\n        self.assertEqual(max_sliding_window(array, k=7), [6, 7])\n\n        array = [8, 5, 10, 7, 9, 4, 15, 12, 90, 13]\n        self.assertEqual(max_sliding_window(array, k=4), [10, 10, 10, 15, 15, 90, 90])\n        self.assertEqual(max_sliding_window(array, k=7), [15, 15, 90, 90])\n        self.assertEqual(\n            max_sliding_window(array, k=2), [8, 10, 10, 9, 9, 15, 15, 90, 90]\n        )\n\n    def test_reconstruct_queue(self):\n        self.assertEqual(\n            [[5, 0], [7, 0], [5, 2], [6, 1], [4, 4], [7, 1]],\n            reconstruct_queue([[7, 0], [4, 4], [7, 1], [5, 0], [6, 1], [5, 2]]),\n        )\n\n\nclass TestPriorityQueue(unittest.TestCase):\n    \"\"\"Test suite for the PriorityQueue data structures.\"\"\"\n\n    def test_priority_queue(self):\n        queue = PriorityQueue([3, 4, 1, 6])\n        self.assertEqual(4, queue.size())\n        self.assertEqual(1, queue.pop())\n        self.assertEqual(3, queue.size())\n        queue.push(2)\n        self.assertEqual(4, queue.size())\n        self.assertEqual(2, queue.pop())\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_searching.py",
    "content": "import unittest\n\nfrom algorithms.searching import (\n    binary_search,\n    binary_search_recur,\n    find_min_rotate,\n    find_min_rotate_recur,\n    first_occurrence,\n    interpolation_search,\n    jump_search,\n    last_occurrence,\n    linear_search,\n    next_greatest_letter,\n    next_greatest_letter_v1,\n    next_greatest_letter_v2,\n    search_insert,\n    search_range,\n    search_rotate,\n    search_rotate_recur,\n    ternary_search,\n    two_sum,\n    two_sum1,\n    two_sum2,\n)\n\n\nclass TestSuite(unittest.TestCase):\n    def test_first_occurrence(self):\n        def helper(array, query):\n            idx = array.index(query) if query in array else -1\n            return idx\n\n        array = [1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6]\n        self.assertEqual(first_occurrence(array, 1), helper(array, 1))\n        self.assertEqual(first_occurrence(array, 3), helper(array, 3))\n        self.assertEqual(first_occurrence(array, 5), helper(array, 5))\n        self.assertEqual(first_occurrence(array, 6), helper(array, 6))\n        self.assertEqual(first_occurrence(array, 7), helper(array, 7))\n        self.assertEqual(first_occurrence(array, -1), helper(array, -1))\n\n    def test_binary_search(self):\n        array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6]\n        self.assertEqual(10, binary_search(array, 5))\n        self.assertEqual(11, binary_search(array, 6))\n        self.assertEqual(-1, binary_search(array, 7))\n        self.assertEqual(-1, binary_search(array, -1))\n        # Test binary_search_recur\n        self.assertEqual(10, binary_search_recur(array, 0, 11, 5))\n        self.assertEqual(11, binary_search_recur(array, 0, 11, 6))\n        self.assertEqual(-1, binary_search_recur(array, 0, 11, 7))\n        self.assertEqual(-1, binary_search_recur(array, 0, 11, -1))\n\n    def test_ternary_search(self):\n        array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6]\n        self.assertEqual(10, ternary_search(0, 11, 5, array))\n        self.assertEqual(3, ternary_search(0, 10, 3, array))\n        self.assertEqual(10, ternary_search(0, 10, 5, array))\n        self.assertEqual(-1, ternary_search(0, 11, 7, array))\n        self.assertEqual(-1, ternary_search(0, 11, -1, array))\n\n    def test_last_occurrence(self):\n        array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6]\n        self.assertEqual(5, last_occurrence(array, 3))\n        self.assertEqual(10, last_occurrence(array, 5))\n        self.assertEqual(-1, last_occurrence(array, 7))\n        self.assertEqual(0, last_occurrence(array, 1))\n        self.assertEqual(13, last_occurrence(array, 6))\n\n    def test_linear_search(self):\n        array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 6, 6]\n        self.assertEqual(6, linear_search(array, 4))\n        self.assertEqual(10, linear_search(array, 5))\n        self.assertEqual(-1, linear_search(array, 7))\n        self.assertEqual(-1, linear_search(array, -1))\n\n    def test_search_insert(self):\n        array = [1, 3, 5, 6]\n        self.assertEqual(2, search_insert(array, 5))\n        self.assertEqual(1, search_insert(array, 2))\n        self.assertEqual(4, search_insert(array, 7))\n        self.assertEqual(0, search_insert(array, 0))\n\n    def test_two_sum(self):\n        array = [2, 7, 11, 15]\n        # test two_sum\n        self.assertEqual([1, 2], two_sum(array, 9))\n        self.assertEqual([2, 4], two_sum(array, 22))\n        # test two_sum1\n        self.assertEqual([1, 2], two_sum1(array, 9))\n        self.assertEqual([2, 4], two_sum1(array, 22))\n        # test two_sum2\n        self.assertEqual([1, 2], two_sum2(array, 9))\n        self.assertEqual([2, 4], two_sum2(array, 22))\n\n    def test_search_range(self):\n        array = [5, 7, 7, 8, 8, 8, 10]\n        self.assertEqual([3, 5], search_range(array, 8))\n        self.assertEqual([1, 2], search_range(array, 7))\n        self.assertEqual([-1, -1], search_range(array, 11))\n\n        array = [5, 7, 7, 7, 7, 8, 8, 8, 8, 10]\n        self.assertEqual([5, 8], search_range(array, 8))\n        self.assertEqual([1, 4], search_range(array, 7))\n        self.assertEqual([-1, -1], search_range(array, 11))\n\n    def test_find_min_rotate(self):\n        array = [4, 5, 6, 7, 0, 1, 2]\n        self.assertEqual(0, find_min_rotate(array))\n        array = [10, 20, -1, 0, 1, 2, 3, 4, 5]\n        self.assertEqual(-1, find_min_rotate(array))\n        # Test find min using recursion\n        array = [4, 5, 6, 7, 0, 1, 2]\n        self.assertEqual(0, find_min_rotate_recur(array, 0, 6))\n        array = [10, 20, -1, 0, 1, 2, 3, 4, 5]\n        self.assertEqual(-1, find_min_rotate_recur(array, 0, 8))\n\n    def test_search_rotate(self):\n        array = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14]\n        self.assertEqual(8, search_rotate(array, 5))\n        self.assertEqual(-1, search_rotate(array, 9))\n        self.assertEqual(8, search_rotate_recur(array, 0, 11, 5))\n        self.assertEqual(-1, search_rotate_recur(array, 0, 11, 9))\n\n    def test_jump_search(self):\n        array = [1, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6]\n        self.assertEqual(10, jump_search(array, 5))\n        self.assertEqual(2, jump_search(array, 3))\n        self.assertEqual(-1, jump_search(array, 7))\n        self.assertEqual(-1, jump_search(array, -1))\n\n    def test_next_greatest_letter(self):\n        letters = [\"c\", \"f\", \"j\"]\n        target = \"a\"\n        self.assertEqual(\"c\", next_greatest_letter(letters, target))\n        self.assertEqual(\"c\", next_greatest_letter_v1(letters, target))\n        self.assertEqual(\"c\", next_greatest_letter_v2(letters, target))\n        letters = [\"c\", \"f\", \"j\"]\n        target = \"d\"\n        self.assertEqual(\"f\", next_greatest_letter(letters, target))\n        self.assertEqual(\"f\", next_greatest_letter_v1(letters, target))\n        self.assertEqual(\"f\", next_greatest_letter_v2(letters, target))\n        letters = [\"c\", \"f\", \"j\"]\n        target = \"j\"\n        self.assertEqual(\"c\", next_greatest_letter(letters, target))\n        self.assertEqual(\"c\", next_greatest_letter_v1(letters, target))\n        self.assertEqual(\"c\", next_greatest_letter_v2(letters, target))\n\n    def test_interpolation_search(self):\n        array = [0, 3, 5, 5, 9, 12, 12, 15, 16, 19, 20]\n        self.assertEqual(1, interpolation_search(array, 3))\n        self.assertEqual(2, interpolation_search(array, 5))\n        self.assertEqual(6, interpolation_search(array, 12))\n        self.assertEqual(-1, interpolation_search(array, 22))\n        self.assertEqual(-1, interpolation_search(array, -10))\n        self.assertEqual(10, interpolation_search(array, 20))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_set.py",
    "content": "import unittest\n\nfrom algorithms.set import find_keyboard_row\n\n\nclass TestFindKeyboardRow(unittest.TestCase):\n    def test_find_keyboard_row(self):\n        self.assertEqual(\n            [\"Alaska\", \"Dad\"], find_keyboard_row([\"Hello\", \"Alaska\", \"Dad\", \"Peace\"])\n        )\n"
  },
  {
    "path": "tests/test_sorting.py",
    "content": "import unittest\n\nfrom algorithms.sorting import (\n    bitonic_sort,\n    bogo_sort,\n    bubble_sort,\n    bucket_sort,\n    cocktail_shaker_sort,\n    comb_sort,\n    counting_sort,\n    cycle_sort,\n    exchange_sort,\n    gnome_sort,\n    insertion_sort,\n    max_heap_sort,\n    merge_sort,\n    min_heap_sort,\n    pancake_sort,\n    pigeonhole_sort,\n    quick_sort,\n    radix_sort,\n    selection_sort,\n    shell_sort,\n)\n\n\ndef is_sorted(array):\n    \"\"\"\n    Helper function to check if the given array is sorted.\n    :param array: Array to check if sorted\n    :return: True if sorted in ascending order, else False\n    \"\"\"\n    return all(array[i] <= array[i + 1] for i in range(len(array) - 1))\n\n\nclass TestSuite(unittest.TestCase):\n    def test_bogo_sort(self):\n        self.assertTrue(is_sorted(bogo_sort([1, 23, 5])))\n\n    def test_bitonic_sort(self):\n        self.assertTrue(is_sorted(bitonic_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_bubble_sort(self):\n        self.assertTrue(is_sorted(bubble_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_comb_sort(self):\n        self.assertTrue(is_sorted(comb_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_counting_sort(self):\n        self.assertTrue(is_sorted(counting_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_cycle_sort(self):\n        self.assertTrue(is_sorted(cycle_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_exchange_sort(self):\n        self.assertTrue(is_sorted(exchange_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_heap_sort(self):\n        self.assertTrue(is_sorted(max_heap_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n        self.assertTrue(is_sorted(min_heap_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_insertion_sort(self):\n        self.assertTrue(is_sorted(insertion_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_merge_sort(self):\n        self.assertTrue(is_sorted(merge_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_pancake_sort(self):\n        self.assertTrue(is_sorted(pancake_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_pigeonhole_sort(self):\n        self.assertTrue(is_sorted(pigeonhole_sort([1, 5, 65, 23, 57, 1232])))\n\n    def test_quick_sort(self):\n        self.assertTrue(is_sorted(quick_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_selection_sort(self):\n        self.assertTrue(is_sorted(selection_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_bucket_sort(self):\n        self.assertTrue(is_sorted(bucket_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_shell_sort(self):\n        self.assertTrue(is_sorted(shell_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_radix_sort(self):\n        self.assertTrue(is_sorted(radix_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_gnome_sort(self):\n        self.assertTrue(is_sorted(gnome_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n    def test_cocktail_shaker_sort(self):\n        self.assertTrue(is_sorted(cocktail_shaker_sort([1, 3, 2, 5, 65, 23, 57, 1232])))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_stack.py",
    "content": "import unittest\n\nfrom algorithms.stack import (\n    ArrayStack,\n    LinkedListStack,\n    OrderedStack,\n    first_is_consecutive,\n    first_stutter,\n    first_switch_pairs,\n    is_sorted,\n    is_valid,\n    remove_min,\n    second_is_consecutive,\n    second_stutter,\n    second_switch_pairs,\n    simplify_path,\n)\n\n\nclass TestSuite(unittest.TestCase):\n    def test_is_consecutive(self):\n        self.assertTrue(first_is_consecutive([3, 4, 5, 6, 7]))\n        self.assertFalse(first_is_consecutive([3, 4, 6, 7]))\n        self.assertFalse(first_is_consecutive([3, 2, 1]))\n\n        self.assertTrue(second_is_consecutive([3, 4, 5, 6, 7]))\n        self.assertFalse(second_is_consecutive([3, 4, 6, 7]))\n        self.assertFalse(second_is_consecutive([3, 2, 1]))\n\n    def test_is_sorted(self):\n        # Test case: bottom [6, 3, 5, 1, 2, 4] top\n        self.assertFalse(is_sorted([6, 3, 5, 1, 2, 4]))\n        self.assertTrue(is_sorted([1, 2, 3, 4, 5, 6]))\n        self.assertFalse(is_sorted([3, 4, 7, 8, 5, 6]))\n\n    def test_remove_min(self):\n        # Test case: bottom [2, 8, 3, -6, 7, 3] top\n        self.assertEqual([2, 8, 3, 7, 3], remove_min([2, 8, 3, -6, 7, 3]))\n        # Test case: 2 smallest value [2, 8, 3, 7, 3]\n        self.assertEqual([4, 8, 7], remove_min([4, 8, 3, 7, 3]))\n\n    def test_stutter(self):\n        # Test case: bottom [3, 7, 1, 14, 9] top\n        self.assertEqual(\n            [3, 3, 7, 7, 1, 1, 14, 14, 9, 9], first_stutter([3, 7, 1, 14, 9])\n        )\n        self.assertEqual(\n            [3, 3, 7, 7, 1, 1, 14, 14, 9, 9], second_stutter([3, 7, 1, 14, 9])\n        )\n\n    def test_switch_pairs(self):\n        # Test case: even number of values in stack\n        # bottom [3, 8, 17, 9, 1, 10] top\n        self.assertEqual([8, 3, 9, 17, 10, 1], first_switch_pairs([3, 8, 17, 9, 1, 10]))\n        self.assertEqual(\n            [8, 3, 9, 17, 10, 1], second_switch_pairs([3, 8, 17, 9, 1, 10])\n        )\n        # Test case: odd number of values in stack\n        # bottom [3, 8, 17, 9, 1] top\n        self.assertEqual([8, 3, 9, 17, 1], first_switch_pairs([3, 8, 17, 9, 1]))\n        self.assertEqual([8, 3, 9, 17, 1], second_switch_pairs([3, 8, 17, 9, 1]))\n\n    def test_is_valid_parenthesis(self):\n\n        self.assertTrue(is_valid(\"[]\"))\n        self.assertTrue(is_valid(\"[]()[]\"))\n        self.assertFalse(is_valid(\"[[[]]\"))\n        self.assertTrue(is_valid(\"{([])}\"))\n        self.assertFalse(is_valid(\"(}\"))\n\n    def test_simplify_path(self):\n        p = \"/my/name/is/..//keon\"\n        self.assertEqual(\"/my/name/keon\", simplify_path(p))\n\n\nclass TestStack(unittest.TestCase):\n    def test_array_stack(self):\n        stack = ArrayStack()\n        stack.push(1)\n        stack.push(2)\n        stack.push(3)\n\n        # test __iter__()\n        it = iter(stack)\n        self.assertEqual(3, next(it))\n        self.assertEqual(2, next(it))\n        self.assertEqual(1, next(it))\n        self.assertRaises(StopIteration, next, it)\n\n        # test __len__()\n        self.assertEqual(3, len(stack))\n\n        # test __str__()\n        self.assertEqual(str(stack), \"Top-> 3 2 1\")\n\n        # test is_empty()\n        self.assertFalse(stack.is_empty())\n\n        # test peek()\n        self.assertEqual(3, stack.peek())\n\n        # test pop()\n        self.assertEqual(3, stack.pop())\n        self.assertEqual(2, stack.pop())\n        self.assertEqual(1, stack.pop())\n\n        self.assertTrue(stack.is_empty())\n\n    def test_linked_list_stack(self):\n        stack = LinkedListStack()\n\n        stack.push(1)\n        stack.push(2)\n        stack.push(3)\n\n        # test __iter__()\n        it = iter(stack)\n        self.assertEqual(3, next(it))\n        self.assertEqual(2, next(it))\n        self.assertEqual(1, next(it))\n        self.assertRaises(StopIteration, next, it)\n\n        # test __len__()\n        self.assertEqual(3, len(stack))\n\n        # test __str__()\n        self.assertEqual(str(stack), \"Top-> 3 2 1\")\n\n        # test is_empty()\n        self.assertFalse(stack.is_empty())\n\n        # test peek()\n        self.assertEqual(3, stack.peek())\n\n        # test pop()\n        self.assertEqual(3, stack.pop())\n        self.assertEqual(2, stack.pop())\n        self.assertEqual(1, stack.pop())\n\n        self.assertTrue(stack.is_empty())\n\n\nclass TestOrderedStack(unittest.TestCase):\n    def test_ordered_stack(self):\n        stack = OrderedStack()\n        self.assertTrue(stack.is_empty())\n        stack.push(1)\n        stack.push(4)\n        stack.push(3)\n        stack.push(6)\n        \"bottom - > 1 3 4 6 \"\n        self.assertEqual(6, stack.pop())\n        self.assertEqual(4, stack.peek())\n        self.assertEqual(3, stack.size())\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_streaming.py",
    "content": "import unittest\n\nfrom algorithms.streaming import one_sparse\nfrom algorithms.streaming.misra_gries import (\n    misras_gries,\n)\n\n\nclass TestMisraGreis(unittest.TestCase):\n    def test_misra_correct(self):\n        self.assertEqual({\"4\": 5}, misras_gries([1, 4, 4, 4, 5, 4, 4]))\n        self.assertEqual({\"1\": 4}, misras_gries([0, 0, 0, 1, 1, 1, 1]))\n        self.assertEqual({\"0\": 4, \"1\": 3}, misras_gries([0, 0, 0, 0, 1, 1, 1, 2, 2], 3))\n\n    def test_misra_incorrect(self):\n        self.assertEqual(None, misras_gries([1, 2, 5, 4, 5, 4, 4, 5, 4, 4, 5]))\n        self.assertEqual(None, misras_gries([0, 0, 0, 2, 1, 1, 1]))\n        self.assertEqual(None, misras_gries([0, 0, 0, 1, 1, 1]))\n\n\nclass TestOneSparse(unittest.TestCase):\n    def test_one_sparse_correct(self):\n        self.assertEqual(\n            4, one_sparse([(4, \"+\"), (2, \"+\"), (2, \"-\"), (4, \"+\"), (3, \"+\"), (3, \"-\")])\n        )\n        self.assertEqual(\n            2,\n            one_sparse(\n                [(2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\")]\n            ),\n        )\n\n    def test_one_sparse_incorrect(self):\n        self.assertEqual(\n            None,\n            one_sparse(\n                [(2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (2, \"+\"), (1, \"+\")]\n            ),\n        )  # Two values remaining\n        self.assertEqual(\n            None,\n            one_sparse(\n                [\n                    (2, \"+\"),\n                    (2, \"+\"),\n                    (2, \"+\"),\n                    (2, \"+\"),\n                    (2, \"-\"),\n                    (2, \"-\"),\n                    (2, \"-\"),\n                    (2, \"-\"),\n                ]\n            ),\n        )  # No values remaining\n        # Bitsum sum of sign is inccorect\n        self.assertEqual(None, one_sparse([(2, \"+\"), (2, \"+\"), (4, \"+\"), (4, \"+\")]))\n"
  },
  {
    "path": "tests/test_string.py",
    "content": "import unittest\n\nfrom algorithms.string import (\n    add_binary,\n    atbash,\n    bracket,\n    caesar_cipher,\n    check_pangram,\n    contain_string,\n    convert_morse_word,\n    count_binary_substring,\n    decode,\n    decode_string,\n    delete_reoccurring_characters,\n    domain_name_1,\n    domain_name_2,\n    encode,\n    first_unique_char,\n    fizzbuzz,\n    group_anagrams,\n    int_to_roman,\n    is_merge_iterative,\n    is_merge_recursive,\n    is_one_edit,\n    is_one_edit2,\n    is_palindrome,\n    is_palindrome_deque,\n    is_palindrome_reverse,\n    is_palindrome_stack,\n    is_palindrome_two_pointer,\n    is_rotated,\n    is_rotated_v1,\n    is_valid_coordinates_0,\n    iterative,\n    judge_circle,\n    knuth_morris_pratt,\n    license_number,\n    longest_common_prefix_v1,\n    longest_common_prefix_v2,\n    longest_common_prefix_v3,\n    longest_palindrome,\n    make_sentence,\n    match_symbol,\n    match_symbol_1,\n    min_distance,\n    min_distance_dp,\n    multiply,\n    panagram,\n    pythonic,\n    rabin_karp,\n    recursive,\n    repeat_string,\n    repeat_substring,\n    reverse_vowel,\n    reverse_words,\n    roman_to_int,\n    rotate,\n    rotate_alt,\n    strip_url_params1,\n    strip_url_params2,\n    strip_url_params3,\n    strong_password,\n    text_justification,\n    ultra_pythonic,\n    unique_morse,\n    word_squares,\n)\n\n\nclass TestAddBinary(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file add_binary.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_add_binary(self):\n        self.assertEqual(\"100\", add_binary(\"11\", \"1\"))\n        self.assertEqual(\"101\", add_binary(\"100\", \"1\"))\n        self.assertEqual(\"10\", add_binary(\"1\", \"1\"))\n\n\nclass TestBreakingBad(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file breaking_bad.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def setUp(self):\n        self.words = [\"Amazon\", \"Microsoft\", \"Google\"]\n        self.symbols = [\"i\", \"Am\", \"cro\", \"le\", \"abc\"]\n        self.result = [\"M[i]crosoft\", \"[Am]azon\", \"Mi[cro]soft\", \"Goog[le]\"]\n\n    def test_match_symbol(self):\n        self.assertEqual(self.result, match_symbol(self.words, self.symbols))\n\n    def test_match_symbol_1(self):\n        self.assertEqual(\n            [\"[Am]azon\", \"Mi[cro]soft\", \"Goog[le]\"],\n            match_symbol_1(self.words, self.symbols),\n        )\n\n    def test_bracket(self):\n        self.assertEqual(\n            (\"[Am]azon\", \"Mi[cro]soft\", \"Goog[le]\"), bracket(self.words, self.symbols)\n        )\n        self.assertEqual(\n            (\"Amazon\", \"Microsoft\", \"Google\"),\n            bracket(self.words, [\"thisshouldnotmatch\"]),\n        )\n        self.assertEqual(\n            (\"Amazon\", \"M[i]crosoft\", \"Google\"), bracket(self.words, [\"i\", \"i\"])\n        )\n\n\nclass TestDecodeString(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file decode_string.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_decode_string(self):\n        self.assertEqual(\"aaabcbc\", decode_string(\"3[a]2[bc]\"))\n        self.assertEqual(\"accaccacc\", decode_string(\"3[a2[c]]\"))\n\n\nclass TestDeleteReoccurring(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file delete_reoccurring.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_delete_reoccurring_characters(self):\n        self.assertEqual(\"abc\", delete_reoccurring_characters(\"aaabcccc\"))\n\n\nclass TestDomainExtractor(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file domain_extractor.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_valid(self):\n        self.assertEqual(domain_name_1(\"https://github.com/SaadBenn\"), \"github\")\n\n    def test_invalid(self):\n        self.assertEqual(domain_name_2(\"http://google.com\"), \"google\")\n\n\nclass TestEncodeDecode(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file encode_decode.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_encode(self):\n        self.assertEqual(\"4:keon2:is7:awesome\", encode(\"keon is awesome\"))\n\n    def test_decode(self):\n        self.assertEqual([\"keon\", \"is\", \"awesome\"], decode(\"4:keon2:is7:awesome\"))\n\n\nclass TestGroupAnagrams(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file group_anagrams.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_group_anagrams(self):\n        self.assertEqual(\n            [[\"eat\", \"tea\", \"ate\"], [\"tan\", \"nat\"], [\"bat\"]],\n            group_anagrams([\"eat\", \"tea\", \"tan\", \"ate\", \"nat\", \"bat\"]),\n        )\n\n\nclass TestIntToRoman(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file int_to_roman.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_int_to_roman(self):\n        self.assertEqual(\"DCXLIV\", int_to_roman(644))\n        self.assertEqual(\"I\", int_to_roman(1))\n        self.assertEqual(\"MMMCMXCIX\", int_to_roman(3999))\n\n\nclass TestIsPalindrome(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file is_palindrome.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_palindrome(self):\n        # 'Otto' is a old german name.\n        self.assertTrue(is_palindrome(\"Otto\"))\n        self.assertFalse(is_palindrome(\"house\"))\n\n    def test_is_palindrome_reverse(self):\n        # 'Otto' is a old german name.\n        self.assertTrue(is_palindrome_reverse(\"Otto\"))\n        self.assertFalse(is_palindrome_reverse(\"house\"))\n\n    def test_is_palindrome_two_pointer(self):\n        # 'Otto' is a old german name.\n        self.assertTrue(is_palindrome_two_pointer(\"Otto\"))\n        self.assertFalse(is_palindrome_two_pointer(\"house\"))\n\n    def test_is_palindrome_stack(self):\n        # 'Otto' is a old german name.\n        self.assertTrue(is_palindrome_stack(\"Otto\"))\n        self.assertFalse(is_palindrome_stack(\"house\"))\n\n    def test_is_palindrome_deque(self):\n        # 'Otto' is a old german name.\n        self.assertTrue(is_palindrome_deque(\"Otto\"))\n        self.assertFalse(is_palindrome_deque(\"house\"))\n\n\nclass TestIsRotated(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file is_rotated.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_rotated(self):\n        self.assertTrue(is_rotated(\"hello\", \"hello\"))\n        self.assertTrue(is_rotated(\"hello\", \"llohe\"))\n        self.assertFalse(is_rotated(\"hello\", \"helol\"))\n        self.assertFalse(is_rotated(\"hello\", \"lloh\"))\n        self.assertTrue(is_rotated(\"\", \"\"))\n\n    def test_is_rotated_v1(self):\n        self.assertTrue(is_rotated_v1(\"hello\", \"hello\"))\n        self.assertTrue(is_rotated_v1(\"hello\", \"llohe\"))\n        self.assertFalse(is_rotated_v1(\"hello\", \"helol\"))\n        self.assertFalse(is_rotated_v1(\"hello\", \"lloh\"))\n        self.assertTrue(is_rotated_v1(\"\", \"\"))\n\n\nclass TestRotated(unittest.TestCase):\n    def test_rotate(self):\n        self.assertEqual(\"llohe\", rotate(\"hello\", 2))\n        self.assertEqual(\"hello\", rotate(\"hello\", 5))\n        self.assertEqual(\"elloh\", rotate(\"hello\", 6))\n        self.assertEqual(\"llohe\", rotate(\"hello\", 7))\n\n    def test_rotate_alt(self):\n        self.assertEqual(\"llohe\", rotate_alt(\"hello\", 2))\n        self.assertEqual(\"hello\", rotate_alt(\"hello\", 5))\n        self.assertEqual(\"elloh\", rotate_alt(\"hello\", 6))\n        self.assertEqual(\"llohe\", rotate_alt(\"hello\", 7))\n\n\nclass TestLicenseNumber(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file license_number.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_license_number(self):\n        self.assertEqual(\"a-b-c-d-f-d-d-f\", license_number(\"a-bc-dfd-df\", 1))\n        self.assertEqual(\"ab-cd-fd-df\", license_number(\"a-bc-dfd-df\", 2))\n        self.assertEqual(\"ab-cdf-ddf\", license_number(\"a-bc-dfd-df\", 3))\n        self.assertEqual(\"abcd-fddf\", license_number(\"a-bc-dfd-df\", 4))\n        self.assertEqual(\"abc-dfddf\", license_number(\"a-bc-dfd-df\", 5))\n\n\nclass TestMakeSentence(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file make_sentence.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_make_sentence(self):\n        dictionarys = [\"\", \"app\", \"let\", \"t\", \"apple\", \"applet\"]\n        word = \"applet\"\n        self.assertTrue(make_sentence(word, dictionarys))\n\n\nclass TestMergeStringChecker(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file merge_string_checker.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_merge_recursive(self):\n        self.assertTrue(is_merge_recursive(\"codewars\", \"cdw\", \"oears\"))\n\n    def test_is_merge_iterative(self):\n        self.assertTrue(is_merge_iterative(\"codewars\", \"cdw\", \"oears\"))\n\n\nclass TestMultiplyStrings(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file multiply_strings.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_multiply(self):\n        self.assertEqual(\"23\", multiply(\"1\", \"23\"))\n        self.assertEqual(\"529\", multiply(\"23\", \"23\"))\n        self.assertEqual(\"0\", multiply(\"0\", \"23\"))\n        self.assertEqual(\"1000000\", multiply(\"100\", \"10000\"))\n\n\nclass TestOneEditDistance(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file one_edit_distance.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_is_one_edit(self):\n        self.assertTrue(is_one_edit(\"abc\", \"abd\"))\n        self.assertFalse(is_one_edit(\"abc\", \"aed\"))\n        self.assertFalse(is_one_edit(\"abcd\", \"abcd\"))\n\n    def test_is_one_edit2(self):\n        self.assertTrue(is_one_edit2(\"abc\", \"abd\"))\n        self.assertFalse(is_one_edit2(\"abc\", \"aed\"))\n        self.assertFalse(is_one_edit2(\"abcd\", \"abcd\"))\n\n\nclass TestRabinKarp(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file rabin_karp.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_rabin_karp(self):\n        self.assertEqual(3, rabin_karp(\"abc\", \"zsnabckfkd\"))\n        self.assertEqual(None, rabin_karp(\"abc\", \"zsnajkskfkd\"))\n\n\nclass TestReverseString(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file reverse_string.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_recursive(self):\n        self.assertEqual(\"ereht olleh\", recursive(\"hello there\"))\n\n    def test_iterative(self):\n        self.assertEqual(\"ereht olleh\", iterative(\"hello there\"))\n\n    def test_pythonic(self):\n        self.assertEqual(\"ereht olleh\", pythonic(\"hello there\"))\n\n    def test_ultra_pythonic(self):\n        self.assertEqual(\"ereht olleh\", ultra_pythonic(\"hello there\"))\n\n\nclass TestReverseVowel(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file reverse_vowel.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_reverse_vowel(self):\n        self.assertEqual(\"holle\", reverse_vowel(\"hello\"))\n\n\nclass TestReverseWords(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file reverse_words.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_reverse_words(self):\n        self.assertEqual(\n            \"pizza like I and kim keon am I\",\n            reverse_words(\"I am keon kim and I like pizza\"),\n        )\n\n\nclass TestRomanToInt(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file roman_to_int.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_roman_to_int(self):\n        self.assertEqual(621, roman_to_int(\"DCXXI\"))\n        self.assertEqual(1, roman_to_int(\"I\"))\n        self.assertEqual(3999, roman_to_int(\"MMMCMXCIX\"))\n\n\nclass TestStripUrlParams(unittest.TestCase):\n    \"\"\"Tests for strip_url_params.py — all three implementations.\"\"\"\n\n    def test_strip_url_params1(self):\n        self.assertEqual(\n            strip_url_params1(\"www.saadbenn.com?a=1&b=2&a=2\"),\n            \"www.saadbenn.com?a=1&b=2\",\n        )\n        self.assertEqual(\n            strip_url_params1(\"www.saadbenn.com?a=1&b=2\", [\"b\"]),\n            \"www.saadbenn.com?a=1\",\n        )\n\n    def test_strip_url_params2(self):\n        self.assertEqual(\n            strip_url_params2(\"www.saadbenn.com?a=1&b=2&a=2\"),\n            \"www.saadbenn.com?a=1&b=2\",\n        )\n        self.assertEqual(\n            strip_url_params2(\"www.saadbenn.com?a=1&b=2\", [\"b\"]),\n            \"www.saadbenn.com?a=1\",\n        )\n\n    def test_strip_url_params3(self):\n        self.assertEqual(\n            strip_url_params3(\"www.saadbenn.com?a=1&b=2&a=2\"),\n            \"www.saadbenn.com?a=1&b=2\",\n        )\n        self.assertEqual(\n            strip_url_params3(\"www.saadbenn.com?a=1&b=2\", [\"b\"]),\n            \"www.saadbenn.com?a=1\",\n        )\n\n\nclass TestValidateCoordinates(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file validate_coordinates.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_valid(self):\n        valid_coordinates = [\"-23, 25\", \"4, -3\", \"90, 180\", \"-90, -180\"]\n        for coordinate in valid_coordinates:\n            self.assertTrue(is_valid_coordinates_0(coordinate))\n\n    def test_invalid(self):\n        invalid_coordinates = [\n            \"23.234, - 23.4234\",\n            \"99.234, 12.324\",\n            \"6.325624, 43.34345.345\",\n            \"0, 1,2\",\n            \"23.245, 1e1\",\n        ]\n        for coordinate in invalid_coordinates:\n            self.assertFalse(is_valid_coordinates_0(coordinate))\n\n\nclass TestWordSquares(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file word_squares.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_word_squares(self):\n        self.assertEqual(\n            [[\"wall\", \"area\", \"lead\", \"lady\"], [\"ball\", \"area\", \"lead\", \"lady\"]],\n            word_squares([\"area\", \"lead\", \"wall\", \"lady\", \"ball\"]),\n        )\n\n\nclass TestUniqueMorse(unittest.TestCase):\n    def test_convert_morse_word(self):\n        self.assertEqual(\"--...-.\", convert_morse_word(\"gin\"))\n        self.assertEqual(\"--...--.\", convert_morse_word(\"msg\"))\n\n    def test_unique_morse(self):\n        self.assertEqual(2, unique_morse([\"gin\", \"zen\", \"gig\", \"msg\"]))\n\n\nclass TestJudgeCircle(unittest.TestCase):\n    def test_judge_circle(self):\n        self.assertTrue(judge_circle(\"UDLRUD\"))\n        self.assertFalse(judge_circle(\"LLRU\"))\n\n\nclass TestStrongPassword(unittest.TestCase):\n    def test_strong_password(self):\n        self.assertEqual(3, strong_password(3, \"Ab1\"))\n        self.assertEqual(1, strong_password(11, \"#Algorithms\"))\n\n\nclass TestCaesarCipher(unittest.TestCase):\n    def test_caesar_cipher(self):\n        self.assertEqual(\"Lipps_Asvph!\", caesar_cipher(\"Hello_World!\", 4))\n        self.assertEqual(\"okffng-Qwvb\", caesar_cipher(\"middle-Outz\", 2))\n\n\nclass TestCheckPangram(unittest.TestCase):\n    def test_check_pangram(self):\n        self.assertTrue(check_pangram(\"The quick brown fox jumps over the lazy dog\"))\n        self.assertFalse(check_pangram(\"The quick brown fox\"))\n\n\nclass TestContainString(unittest.TestCase):\n    def test_contain_string(self):\n        self.assertEqual(-1, contain_string(\"mississippi\", \"issipi\"))\n        self.assertEqual(0, contain_string(\"Hello World\", \"\"))\n        self.assertEqual(2, contain_string(\"hello\", \"ll\"))\n\n\nclass TestCountBinarySubstring(unittest.TestCase):\n    def test_count_binary_substring(self):\n        self.assertEqual(6, count_binary_substring(\"00110011\"))\n        self.assertEqual(4, count_binary_substring(\"10101\"))\n        self.assertEqual(3, count_binary_substring(\"00110\"))\n\n\nclass TestRepeatString(unittest.TestCase):\n    def test_repeat_string(self):\n        self.assertEqual(3, repeat_string(\"abcd\", \"cdabcdab\"))\n        self.assertEqual(4, repeat_string(\"bb\", \"bbbbbbb\"))\n\n\nclass TestTextJustification(unittest.TestCase):\n    def test_text_justification(self):\n        self.assertEqual(\n            [\"This    is    an\", \"example  of text\", \"justification.  \"],\n            text_justification(\n                [\"This\", \"is\", \"an\", \"example\", \"of\", \"text\", \"justification.\"], 16\n            ),\n        )\n\n        self.assertEqual(\n            [\"What   must   be\", \"acknowledgment  \", \"shall be        \"],\n            text_justification(\n                [\"What\", \"must\", \"be\", \"acknowledgment\", \"shall\", \"be\"], 16\n            ),\n        )\n\n\nclass TestMinDistance(unittest.TestCase):\n    def test_min_distance(self):\n        self.assertEqual(2, min_distance(\"sea\", \"eat\"))\n        self.assertEqual(6, min_distance(\"abAlgocrithmf\", \"Algorithmmd\"))\n        self.assertEqual(4, min_distance(\"acbbd\", \"aabcd\"))\n\n\nclass TestMinDistanceDP(unittest.TestCase):\n    def test_min_distance(self):\n        self.assertEqual(2, min_distance_dp(\"sea\", \"eat\"))\n        self.assertEqual(6, min_distance_dp(\"abAlgocrithmf\", \"Algorithmmd\"))\n        self.assertEqual(4, min_distance(\"acbbd\", \"aabcd\"))\n\n\nclass TestLongestCommonPrefix(unittest.TestCase):\n    def test_longest_common_prefix(self):\n        # Test first solution\n        self.assertEqual(\"fl\", longest_common_prefix_v1([\"flower\", \"flow\", \"flight\"]))\n        self.assertEqual(\"\", longest_common_prefix_v1([\"dog\", \"racecar\", \"car\"]))\n        # Test second solution\n        self.assertEqual(\"fl\", longest_common_prefix_v2([\"flower\", \"flow\", \"flight\"]))\n        self.assertEqual(\"\", longest_common_prefix_v2([\"dog\", \"racecar\", \"car\"]))\n        # Test third solution\n        self.assertEqual(\"fl\", longest_common_prefix_v3([\"flower\", \"flow\", \"flight\"]))\n        self.assertEqual(\"\", longest_common_prefix_v3([\"dog\", \"racecar\", \"car\"]))\n\n\nclass TestFirstUniqueChar(unittest.TestCase):\n    def test_first_unique_char(self):\n        self.assertEqual(0, first_unique_char(\"leetcode\"))\n        self.assertEqual(2, first_unique_char(\"loveleetcode\"))\n\n\nclass TestRepeatSubstring(unittest.TestCase):\n    def test_repeat_substring(self):\n        self.assertTrue(repeat_substring(\"abab\"))\n        self.assertFalse(repeat_substring(\"aba\"))\n        self.assertTrue(repeat_substring(\"abcabcabcabc\"))\n\n\nclass TestAtbashCipher(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file atbash_cipher.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_atbash_cipher(self):\n        self.assertEqual(\"zyxwvutsrqponml\", atbash(\"abcdefghijklmno\"))\n        self.assertEqual(\"KbgslM\", atbash(\"PythoN\"))\n        self.assertEqual(\"AttaCK at DawN\", atbash(\"ZggzXP zg WzdM\"))\n        self.assertEqual(\"ZggzXP zg WzdM\", atbash(\"AttaCK at DawN\"))\n\n\nclass TestLongestPalindromicSubstring(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file longest_palindromic_substring.py\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_longest_palindromic_substring(self):\n        self.assertEqual(\"bb\", longest_palindrome(\"cbbd\"))\n        self.assertEqual(\"abba\", longest_palindrome(\"abba\"))\n        self.assertEqual(\"asdadsa\", longest_palindrome(\"dasdasdasdasdasdadsa\"))\n        self.assertEqual(\"abba\", longest_palindrome(\"cabba\"))\n\n\nclass TestKnuthMorrisPratt(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file knuth_morris_pratt.py\n\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_knuth_morris_pratt(self):\n        self.assertEqual([0, 1, 2, 3, 4], knuth_morris_pratt(\"aaaaaaa\", \"aaa\"))\n        self.assertEqual([0, 4], knuth_morris_pratt(\"abcdabc\", \"abc\"))\n        self.assertEqual([], knuth_morris_pratt(\"aabcdaab\", \"aba\"))\n        self.assertEqual([0, 4], knuth_morris_pratt([0, 0, 1, 1, 0, 0, 1, 0], [0, 0]))\n\n\nclass TestPanagram(unittest.TestCase):\n    \"\"\"[summary]\n    Test for the file panagram.py\n\n    Arguments:\n        unittest {[type]} -- [description]\n    \"\"\"\n\n    def test_empty_string(self):\n        # Arrange\n        string = \"\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(False, res)\n\n    def test_single_word_non_panagram(self):\n        # Arrange\n        string = \"sentence\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(False, res)\n\n    def test_fox_panagram_no_spaces(self):\n        # Arrange\n        string = \"thequickbrownfoxjumpsoverthelazydog\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(True, res)\n\n    def test_fox_panagram_mixed_case(self):\n        # Arrange\n        string = \"theqUiCkbrOwnfOxjUMPSOVErThELAzYDog\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(True, res)\n\n    def test_whitespace_punctuation(self):\n        # Arrange\n        string = \"\\n\\t\\r,.-_!?\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(False, res)\n\n    def test_fox_panagram(self):\n        # Arrange\n        string = \"the quick brown fox jumps over the lazy dog\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(True, res)\n\n    def test_swedish_panagram(self):\n        # Arrange\n        string = \"Yxmördaren Julia Blomqvist på fäktning i Schweiz\"\n\n        # Act\n        res = panagram(string)\n\n        # Assert\n        self.assertEqual(True, res)\n\n\nclass TestFizzbuzz(unittest.TestCase):\n    \"\"\"[summary]\n    Tests for the fizzbuzz method in file fizzbuzz.py\n    \"\"\"\n\n    def test_fizzbuzz(self):\n        # Testing that n < 0 returns a Value Error\n        self.assertRaises(ValueError, fizzbuzz.fizzbuzz, -2)\n\n        # Testing that a string returns a Type Error.\n        self.assertRaises(TypeError, fizzbuzz.fizzbuzz, \"hello\")\n\n        # Testing a base case, n = 3\n        result = fizzbuzz.fizzbuzz(3)\n        expected = [1, 2, \"Fizz\"]\n        self.assertEqual(result, expected)\n\n        # Testing a base case, n = 5\n        result = fizzbuzz.fizzbuzz(5)\n        expected = [1, 2, \"Fizz\", 4, \"Buzz\"]\n        self.assertEqual(result, expected)\n\n        # Testing a base case, n = 15 i.e. mod 3 and 5\n        result = fizzbuzz.fizzbuzz(15)\n        expected = [\n            1,\n            2,\n            \"Fizz\",\n            4,\n            \"Buzz\",\n            \"Fizz\",\n            7,\n            8,\n            \"Fizz\",\n            \"Buzz\",\n            11,\n            \"Fizz\",\n            13,\n            14,\n            \"FizzBuzz\",\n        ]\n        self.assertEqual(result, expected)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_tree.py",
    "content": "import unittest\n\nfrom algorithms.data_structures.b_tree import BTree\nfrom algorithms.data_structures.fenwick_tree import Fenwick_Tree\nfrom algorithms.tree import construct_tree_postorder_preorder as ctpp\nfrom algorithms.tree.traversal_inorder import inorder, inorder_rec\nfrom algorithms.tree.traversal_postorder import postorder, postorder_rec\nfrom algorithms.tree.traversal_preorder import preorder, preorder_rec\n\n\nclass Node:\n    def __init__(self, val, left=None, right=None):\n        self.val = val\n        self.left = left\n        self.right = right\n\n\nclass TestTraversal(unittest.TestCase):\n    def test_preorder(self):\n        tree = create_tree()\n        self.assertEqual([100, 50, 25, 75, 150, 125, 175], preorder(tree))\n        self.assertEqual([100, 50, 25, 75, 150, 125, 175], preorder_rec(tree))\n\n    def test_postorder(self):\n        tree = create_tree()\n        self.assertEqual([25, 75, 50, 125, 175, 150, 100], postorder(tree))\n        self.assertEqual([25, 75, 50, 125, 175, 150, 100], postorder_rec(tree))\n\n    def test_inorder(self):\n        tree = create_tree()\n        self.assertEqual([25, 50, 75, 100, 125, 150, 175], inorder(tree))\n        self.assertEqual([25, 50, 75, 100, 125, 150, 175], inorder_rec(tree))\n\n\ndef create_tree():\n    n1 = Node(100)\n    n2 = Node(50)\n    n3 = Node(150)\n    n4 = Node(25)\n    n5 = Node(75)\n    n6 = Node(125)\n    n7 = Node(175)\n    n1.left, n1.right = n2, n3\n    n2.left, n2.right = n4, n5\n    n3.left, n3.right = n6, n7\n    return n1\n\n\nclass TestBTree(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        import random\n\n        random.seed(18719)\n        cls.random = random\n        cls.range = 10000\n\n    def setUp(self):\n        self.keys_to_insert = [\n            self.random.randrange(-self.range, self.range) for i in range(self.range)\n        ]\n\n    def test_insertion_and_find_even_degree(self):\n        btree = BTree(4)\n        for i in self.keys_to_insert:\n            btree.insert_key(i)\n\n        for _ in range(100):\n            key = self.random.choice(self.keys_to_insert)\n            self.assertTrue(btree.find(key))\n\n    def test_insertion_and_find_odd_degree(self):\n        btree = BTree(3)\n        for i in self.keys_to_insert:\n            btree.insert_key(i)\n\n        for _ in range(100):\n            key = self.random.choice(self.keys_to_insert)\n            self.assertTrue(btree.find(key))\n\n    def test_deletion_even_degree(self):\n        btree = BTree(4)\n        key_list = set(self.keys_to_insert)\n        for i in key_list:\n            btree.insert_key(i)\n\n        for key in key_list:\n            btree.remove_key(key)\n            self.assertFalse(btree.find(key))\n\n        self.assertEqual(btree.root.keys, [])\n        self.assertEqual(btree.root.children, [])\n\n    def test_deletion_odd_degree(self):\n        btree = BTree(3)\n        key_list = set(self.keys_to_insert)\n        for i in key_list:\n            btree.insert_key(i)\n\n        for key in key_list:\n            btree.remove_key(key)\n            self.assertFalse(btree.find(key))\n\n        self.assertEqual(btree.root.keys, [])\n        self.assertEqual(btree.root.children, [])\n\n\nclass TestConstructTreePreorderPostorder(unittest.TestCase):\n    def test_construct_tree(self):\n\n        # Test 1\n        ctpp.pre_index = 0\n        pre1 = [1, 2, 4, 8, 9, 5, 3, 6, 7]\n        post1 = [8, 9, 4, 5, 2, 6, 7, 3, 1]\n        size1 = len(pre1)\n\n        self.assertEqual(\n            ctpp.construct_tree(pre1, post1, size1), [8, 4, 9, 2, 5, 1, 6, 3, 7]\n        )\n\n        # Test 2\n        ctpp.pre_index = 0\n        pre2 = [1, 2, 4, 5, 3, 6, 7]\n        post2 = [4, 5, 2, 6, 7, 3, 1]\n        size2 = len(pre2)\n\n        self.assertEqual(ctpp.construct_tree(pre2, post2, size2), [4, 2, 5, 1, 6, 3, 7])\n\n        # Test 3\n        ctpp.pre_index = 0\n        pre3 = [12, 7, 16, 21, 5, 1, 9]\n        post3 = [16, 21, 7, 1, 9, 5, 12]\n        size3 = len(pre3)\n\n        self.assertEqual(\n            ctpp.construct_tree(pre3, post3, size3), [16, 7, 21, 12, 1, 5, 9]\n        )\n\n\nclass TestFenwickTree(unittest.TestCase):\n    def test_construct_tree_with_update_1(self):\n        freq = [2, 1, 1, 3, 2, 3, 4, 5, 6, 7, 8, 9]\n        ft = Fenwick_Tree(freq)\n        bit_tree = ft.construct()\n        self.assertEqual(12, ft.get_sum(bit_tree, 5))\n\n        freq[3] += 6\n        ft.update_bit(bit_tree, 3, 6)\n        self.assertEqual(18, ft.get_sum(bit_tree, 5))\n\n    def test_construct_tree_with_update_2(self):\n        freq = [1, 2, 3, 4, 5]\n        ft = Fenwick_Tree(freq)\n        bit_tree = ft.construct()\n        self.assertEqual(10, ft.get_sum(bit_tree, 3))\n\n        freq[3] -= 5\n        ft.update_bit(bit_tree, 3, -5)\n        self.assertEqual(5, ft.get_sum(bit_tree, 3))\n\n    def test_construct_tree_with_update_3(self):\n        freq = [2, 1, 4, 6, -1, 5, -32, 0, 1]\n        ft = Fenwick_Tree(freq)\n        bit_tree = ft.construct()\n        self.assertEqual(12, ft.get_sum(bit_tree, 4))\n\n        freq[2] += 11\n        ft.update_bit(bit_tree, 2, 11)\n        self.assertEqual(23, ft.get_sum(bit_tree, 4))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_veb_tree.py",
    "content": "import unittest\n\nfrom algorithms.data_structures.veb_tree import VEBTree\n\n\nclass TestVEBTree(unittest.TestCase):\n    def setUp(self):\n        self.veb = VEBTree(16)\n\n    def test_insert_and_member(self):\n        values = [2, 3, 4, 7, 14]\n        for v in values:\n            self.veb.insert(v)\n\n        for v in values:\n            self.assertTrue(self.veb.member(v))\n\n        self.assertFalse(self.veb.member(5))\n\n    def test_min_max(self):\n        self.veb.insert(10)\n        self.veb.insert(2)\n        self.veb.insert(15)\n\n        self.assertEqual(2, self.veb.minimum())\n        self.assertEqual(15, self.veb.maximum())\n\n    def test_successor(self):\n        for v in [2, 4, 8, 12]:\n            self.veb.insert(v)\n\n        self.assertEqual(4, self.veb.successor(2))\n        self.assertEqual(8, self.veb.successor(4))\n        self.assertIsNone(self.veb.successor(12))\n\n    def test_delete(self):\n        for v in [1, 3, 5, 7]:\n            self.veb.insert(v)\n\n        self.veb.delete(3)\n        self.assertFalse(self.veb.member(3))\n        self.assertEqual(5, self.veb.successor(1))\n\n    def test_invalid_universe(self):\n        with self.assertRaises(ValueError):\n            VEBTree(15)  # not power of 2\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  }
]