diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 3479b0218a69..02de2b6e89f2 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -11,7 +11,7 @@ jobs: - run: pip install codespell flake8 - run: | SKIP="./.*,./other/dictionary.txt,./other/words,./project_euler/problem_22/p022_names.txt" - codespell -L ans,fo,hist,iff,secant,tim --skip=$SKIP --quiet-level=2 + codespell --ignore-words-list=ans,fo,hist,iff,secant,som,tim --skip=$SKIP --quiet-level=2 - name: Codespell comment if: ${{ failure() }} uses: plettich/python_codespell_action@master diff --git a/.travis.yml b/.travis.yml index a03f8161f13e..d2394b4097f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,16 +5,23 @@ python: 3.8 cache: pip before_install: pip install --upgrade pip setuptools six install: pip install black flake8 +jobs: + include: + - name: Build + before_script: + - black --check . || true + - flake8 --ignore=E203,W503 --max-complexity=25 --max-line-length=88 --statistics --count . + - scripts/validate_filenames.py # no uppercase, no spaces, in a directory + - pip install -r requirements.txt # fast fail on black, flake8, validate_filenames + script: + - mypy --ignore-missing-imports . || true # https://github.com/python/mypy/issues/7907 + - pytest --doctest-modules --ignore=project_euler/ --durations=10 --cov-report=term-missing:skip-covered --cov=. . + - name: Project Euler + before_script: pip install pytest-cov + script: + - pytest --doctest-modules --durations=10 --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ +after_success: + - scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md notifications: webhooks: https://www.travisbuddy.com/ on_success: never -before_script: - - black --check . || true - - flake8 --ignore=E203,W503 --max-complexity=25 --max-line-length=88 --statistics --count . - - scripts/validate_filenames.py # no uppercase, no spaces, in a directory - - pip install -r requirements.txt # fast fail on black, flake8, validate_filenames -script: - - mypy --ignore-missing-imports . - - pytest --doctest-modules --durations=10 --cov-report=term-missing:skip-covered --cov=. . -after_success: - - scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/DIRECTORY.md b/DIRECTORY.md index 1dceb887940c..3227711c7853 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -25,7 +25,9 @@ * [Sum Of Subsets](https://github.com/TheAlgorithms/Python/blob/master/backtracking/sum_of_subsets.py) ## Bit Manipulation + * [Binary And Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_and_operator.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) + * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) ## Blockchain * [Chinese Remainder Theorem](https://github.com/TheAlgorithms/Python/blob/master/blockchain/chinese_remainder_theorem.py) @@ -86,6 +88,7 @@ ## Conversions * [Binary To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_decimal.py) + * [Binary To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_octal.py) * [Decimal To Any](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_any.py) * [Decimal To Binary](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_binary.py) * [Decimal To Hexadecimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_hexadecimal.py) @@ -131,6 +134,7 @@ * [Deque Doubly](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/deque_doubly.py) * [Doubly Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/doubly_linked_list.py) * [From Sequence](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/from_sequence.py) + * [Has Loop](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/has_loop.py) * [Is Palindrome](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/is_palindrome.py) * [Middle Element Of Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/middle_element_of_linked_list.py) * [Print Reverse](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/print_reverse.py) @@ -141,6 +145,7 @@ * [Circular Queue](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/circular_queue.py) * [Double Ended Queue](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/double_ended_queue.py) * [Linked Queue](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/linked_queue.py) + * [Priority Queue Using List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/priority_queue_using_list.py) * [Queue On List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/queue_on_list.py) * [Queue On Pseudo Stack](https://github.com/TheAlgorithms/Python/blob/master/data_structures/queue/queue_on_pseudo_stack.py) * Stacks @@ -303,7 +308,7 @@ ## Linear Algebra * Src * [Lib](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/lib.py) - * [Polynom-For-Points](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/polynom-for-points.py) + * [Polynom For Points](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/polynom_for_points.py) * [Power Iteration](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/power_iteration.py) * [Rayleigh Quotient](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/rayleigh_quotient.py) * [Test Linear Algebra](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/test_linear_algebra.py) @@ -518,6 +523,7 @@ * [Sol2](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_06/sol2.py) * [Sol3](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_06/sol3.py) * [Sol4](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_06/sol4.py) + * [Test Solutions](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_06/test_solutions.py) * Problem 07 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_07/sol1.py) * [Sol2](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_07/sol2.py) @@ -625,6 +631,9 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_52/sol1.py) * Problem 53 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_53/sol1.py) + * Problem 54 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_54/sol1.py) + * [Test Poker Hand](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_54/test_poker_hand.py) * Problem 55 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_55/sol1.py) * Problem 551 @@ -637,6 +646,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_67/sol1.py) * Problem 76 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_76/sol1.py) + * Problem 97 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_97/sol1.py) * Problem 99 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_99/sol1.py) @@ -648,6 +659,7 @@ ## Searches * [Binary Search](https://github.com/TheAlgorithms/Python/blob/master/searches/binary_search.py) * [Double Linear Search](https://github.com/TheAlgorithms/Python/blob/master/searches/double_linear_search.py) + * [Double Linear Search Recursion](https://github.com/TheAlgorithms/Python/blob/master/searches/double_linear_search_recursion.py) * [Fibonacci Search](https://github.com/TheAlgorithms/Python/blob/master/searches/fibonacci_search.py) * [Hill Climbing](https://github.com/TheAlgorithms/Python/blob/master/searches/hill_climbing.py) * [Interpolation Search](https://github.com/TheAlgorithms/Python/blob/master/searches/interpolation_search.py) @@ -655,7 +667,7 @@ * [Linear Search](https://github.com/TheAlgorithms/Python/blob/master/searches/linear_search.py) * [Quick Select](https://github.com/TheAlgorithms/Python/blob/master/searches/quick_select.py) * [Sentinel Linear Search](https://github.com/TheAlgorithms/Python/blob/master/searches/sentinel_linear_search.py) - * [Simple-Binary-Search](https://github.com/TheAlgorithms/Python/blob/master/searches/simple-binary-search.py) + * [Simple Binary Search](https://github.com/TheAlgorithms/Python/blob/master/searches/simple_binary_search.py) * [Simulated Annealing](https://github.com/TheAlgorithms/Python/blob/master/searches/simulated_annealing.py) * [Tabu Search](https://github.com/TheAlgorithms/Python/blob/master/searches/tabu_search.py) * [Ternary Search](https://github.com/TheAlgorithms/Python/blob/master/searches/ternary_search.py) @@ -689,9 +701,9 @@ * [Radix Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/radix_sort.py) * [Random Normal Distribution Quicksort](https://github.com/TheAlgorithms/Python/blob/master/sorts/random_normal_distribution_quicksort.py) * [Random Pivot Quick Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/random_pivot_quick_sort.py) - * [Recursive-Quick-Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/recursive-quick-sort.py) * [Recursive Bubble Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/recursive_bubble_sort.py) * [Recursive Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/recursive_insertion_sort.py) + * [Recursive Quick Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/recursive_quick_sort.py) * [Selection Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/selection_sort.py) * [Shell Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/shell_sort.py) * [Stooge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/stooge_sort.py) @@ -703,8 +715,9 @@ * [Wiggle Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/wiggle_sort.py) ## Strings - * [Aho-Corasick](https://github.com/TheAlgorithms/Python/blob/master/strings/aho-corasick.py) + * [Aho Corasick](https://github.com/TheAlgorithms/Python/blob/master/strings/aho_corasick.py) * [Boyer Moore Search](https://github.com/TheAlgorithms/Python/blob/master/strings/boyer_moore_search.py) + * [Can String Be Rearranged As Palindrome](https://github.com/TheAlgorithms/Python/blob/master/strings/can_string_be_rearranged_as_palindrome.py) * [Capitalize](https://github.com/TheAlgorithms/Python/blob/master/strings/capitalize.py) * [Check Anagrams](https://github.com/TheAlgorithms/Python/blob/master/strings/check_anagrams.py) * [Check Pangram](https://github.com/TheAlgorithms/Python/blob/master/strings/check_pangram.py) diff --git a/arithmetic_analysis/in_static_equilibrium.py b/arithmetic_analysis/in_static_equilibrium.py index dd7fa706143e..f08b39c3505c 100644 --- a/arithmetic_analysis/in_static_equilibrium.py +++ b/arithmetic_analysis/in_static_equilibrium.py @@ -6,14 +6,14 @@ mypy : passed """ -from typing import List +from __future__ import annotations from numpy import array, cos, cross, radians, sin # type: ignore def polar_force( magnitude: float, angle: float, radian_mode: bool = False -) -> List[float]: +) -> list[float]: """ Resolves force along rectangular components. (force, angle) => (force_x, force_y) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 3956b21a9182..ceaffe3fae76 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -5,11 +5,11 @@ Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring """ -from typing import List +from __future__ import annotations def valid_coloring( - neighbours: List[int], colored_vertices: List[int], color: int + neighbours: list[int], colored_vertices: list[int], color: int ) -> bool: """ For each neighbour check if coloring constraint is satisfied @@ -35,7 +35,7 @@ def valid_coloring( def util_color( - graph: List[List[int]], max_colors: int, colored_vertices: List[int], index: int + graph: list[list[int]], max_colors: int, colored_vertices: list[int], index: int ) -> bool: """ Pseudo-Code @@ -86,7 +86,7 @@ def util_color( return False -def color(graph: List[List[int]], max_colors: int) -> List[int]: +def color(graph: list[list[int]], max_colors: int) -> list[int]: """ Wrapper function to call subroutine called util_color which will either return True or False. diff --git a/backtracking/hamiltonian_cycle.py b/backtracking/hamiltonian_cycle.py index 7be1ea350d7c..bf15cce4aca4 100644 --- a/backtracking/hamiltonian_cycle.py +++ b/backtracking/hamiltonian_cycle.py @@ -6,11 +6,11 @@ Wikipedia: https://en.wikipedia.org/wiki/Hamiltonian_path """ -from typing import List +from __future__ import annotations def valid_connection( - graph: List[List[int]], next_ver: int, curr_ind: int, path: List[int] + graph: list[list[int]], next_ver: int, curr_ind: int, path: list[int] ) -> bool: """ Checks whether it is possible to add next into path by validating 2 statements @@ -47,7 +47,7 @@ def valid_connection( return not any(vertex == next_ver for vertex in path) -def util_hamilton_cycle(graph: List[List[int]], path: List[int], curr_ind: int) -> bool: +def util_hamilton_cycle(graph: list[list[int]], path: list[int], curr_ind: int) -> bool: """ Pseudo-Code Base Case: @@ -108,7 +108,7 @@ def util_hamilton_cycle(graph: List[List[int]], path: List[int], curr_ind: int) return False -def hamilton_cycle(graph: List[List[int]], start_index: int = 0) -> List[int]: +def hamilton_cycle(graph: list[list[int]], start_index: int = 0) -> list[int]: r""" Wrapper function to call subroutine called util_hamilton_cycle, which will either return array of vertices indicating hamiltonian cycle diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index e4a93fbc2105..2413ba468838 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -1,9 +1,9 @@ # Knight Tour Intro: https://www.youtube.com/watch?v=ab_dY3dZFHM -from typing import List, Tuple +from __future__ import annotations -def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[int]]: +def get_valid_pos(position: tuple[int], n: int) -> list[tuple[int]]: """ Find all the valid positions a knight can move to from the current position. @@ -32,7 +32,7 @@ def get_valid_pos(position: Tuple[int], n: int) -> List[Tuple[int]]: return permissible_positions -def is_complete(board: List[List[int]]) -> bool: +def is_complete(board: list[list[int]]) -> bool: """ Check if the board (matrix) has been completely filled with non-zero values. @@ -46,7 +46,7 @@ def is_complete(board: List[List[int]]) -> bool: return not any(elem == 0 for row in board for elem in row) -def open_knight_tour_helper(board: List[List[int]], pos: Tuple[int], curr: int) -> bool: +def open_knight_tour_helper(board: list[list[int]], pos: tuple[int], curr: int) -> bool: """ Helper function to solve knight tour problem. """ @@ -66,7 +66,7 @@ def open_knight_tour_helper(board: List[List[int]], pos: Tuple[int], curr: int) return False -def open_knight_tour(n: int) -> List[List[int]]: +def open_knight_tour(n: int) -> list[list[int]]: """ Find the solution for the knight tour problem for a board of size n. Raises ValueError if the tour cannot be performed for the given size. diff --git a/backtracking/minimax.py b/backtracking/minimax.py index 4cec0e403ddf..91188090c899 100644 --- a/backtracking/minimax.py +++ b/backtracking/minimax.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math """ Minimax helps to achieve maximum score in a game by checking all possible moves @@ -9,26 +11,63 @@ """ -def minimax(Depth, nodeIndex, isMax, scores, height): +def minimax( + depth: int, node_index: int, is_max: bool, scores: list[int], height: float +) -> int: + """ + >>> import math + >>> scores = [90, 23, 6, 33, 21, 65, 123, 34423] + >>> height = math.log(len(scores), 2) + >>> minimax(0, 0, True, scores, height) + 65 + >>> minimax(-1, 0, True, scores, height) + Traceback (most recent call last): + ... + ValueError: Depth cannot be less than 0 + >>> minimax(0, 0, True, [], 2) + Traceback (most recent call last): + ... + ValueError: Scores cannot be empty + >>> scores = [3, 5, 2, 9, 12, 5, 23, 23] + >>> height = math.log(len(scores), 2) + >>> minimax(0, 0, True, scores, height) + 12 + >>> minimax('1', 2, True, [], 2 ) + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + + if depth < 0: + raise ValueError("Depth cannot be less than 0") + + if len(scores) == 0: + raise ValueError("Scores cannot be empty") - if Depth == height: - return scores[nodeIndex] + if depth == height: + return scores[node_index] - if isMax: + if is_max: return max( - minimax(Depth + 1, nodeIndex * 2, False, scores, height), - minimax(Depth + 1, nodeIndex * 2 + 1, False, scores, height), + minimax(depth + 1, node_index * 2, False, scores, height), + minimax(depth + 1, node_index * 2 + 1, False, scores, height), ) + return min( - minimax(Depth + 1, nodeIndex * 2, True, scores, height), - minimax(Depth + 1, nodeIndex * 2 + 1, True, scores, height), + minimax(depth + 1, node_index * 2, True, scores, height), + minimax(depth + 1, node_index * 2 + 1, True, scores, height), ) -if __name__ == "__main__": - +def main(): scores = [90, 23, 6, 33, 21, 65, 123, 34423] height = math.log(len(scores), 2) - print("Optimal value : ", end="") print(minimax(0, 0, True, scores, height)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() diff --git a/backtracking/n_queens_math.py b/backtracking/n_queens_math.py index fb2b74bd7c4a..811611971616 100644 --- a/backtracking/n_queens_math.py +++ b/backtracking/n_queens_math.py @@ -75,14 +75,14 @@ for another one or vice versa. """ -from typing import List +from __future__ import annotations def depth_first_search( - possible_board: List[int], - diagonal_right_collisions: List[int], - diagonal_left_collisions: List[int], - boards: List[List[str]], + possible_board: list[int], + diagonal_right_collisions: list[int], + diagonal_left_collisions: list[int], + boards: list[list[str]], n: int, ) -> None: """ diff --git a/bit_manipulation/binary_and_operator.py b/bit_manipulation/binary_and_operator.py new file mode 100644 index 000000000000..f1b910f8cc9b --- /dev/null +++ b/bit_manipulation/binary_and_operator.py @@ -0,0 +1,52 @@ +# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm + + +def binary_and(a: int, b: int): + """ + Take in 2 integers, convert them to binary, + return a binary number that is the + result of a binary and operation on the integers provided. + + >>> binary_and(25, 32) + '0b000000' + >>> binary_and(37, 50) + '0b100000' + >>> binary_and(21, 30) + '0b10100' + >>> binary_and(58, 73) + '0b0001000' + >>> binary_and(0, 255) + '0b00000000' + >>> binary_and(256, 256) + '0b100000000' + >>> binary_and(0, -1) + Traceback (most recent call last): + ... + ValueError: the value of both input must be positive + >>> binary_and(0, 1.1) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> binary_and("0", "1") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0 or b < 0: + raise ValueError("the value of both input must be positive") + + a_binary = str(bin(a))[2:] # remove the leading "0b" + b_binary = str(bin(b))[2:] # remove the leading "0b" + + max_len = max(len(a_binary), len(b_binary)) + + return "0b" + "".join( + str(int(char_a == "1" and char_b == "1")) + for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len)) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/bit_manipulation/binary_xor_operator.py b/bit_manipulation/binary_xor_operator.py new file mode 100644 index 000000000000..0edf2ba6606d --- /dev/null +++ b/bit_manipulation/binary_xor_operator.py @@ -0,0 +1,52 @@ +# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm + + +def binary_xor(a: int, b: int): + """ + Take in 2 integers, convert them to binary, + return a binary number that is the + result of a binary xor operation on the integers provided. + + >>> binary_xor(25, 32) + '0b111001' + >>> binary_xor(37, 50) + '0b010111' + >>> binary_xor(21, 30) + '0b01011' + >>> binary_xor(58, 73) + '0b1110011' + >>> binary_xor(0, 255) + '0b11111111' + >>> binary_xor(256, 256) + '0b000000000' + >>> binary_xor(0, -1) + Traceback (most recent call last): + ... + ValueError: the value of both input must be positive + >>> binary_xor(0, 1.1) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> binary_xor("0", "1") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0 or b < 0: + raise ValueError("the value of both input must be positive") + + a_binary = str(bin(a))[2:] # remove the leading "0b" + b_binary = str(bin(b))[2:] # remove the leading "0b" + + max_len = max(len(a_binary), len(b_binary)) + + return "0b" + "".join( + str(int(char_a != char_b)) + for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len)) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/blockchain/mckinsey_blame.py b/blockchain/mckinsey_blame.py new file mode 100644 index 000000000000..a55b624483ca --- /dev/null +++ b/blockchain/mckinsey_blame.py @@ -0,0 +1,168 @@ +def compare_string(string1, string2): + """ + >>> compare_string('0010','0110') + '0_10' + + >>> compare_string('0110','1101') + -1 + """ + l1 = list(string1) + l2 = list(string2) + count = 0 + for i in range(len(l1)): + if l1[i] != l2[i]: + count += 1 + l1[i] = "_" + if count > 1: + return -1 + else: + return "".join(l1) + + +def check(binary): + """ + >>> check(['0.00.01.5']) + ['0.00.01.5'] + """ + pi = [] + while 1: + check1 = ["$"] * len(binary) + temp = [] + for i in range(len(binary)): + for j in range(i + 1, len(binary)): + k = compare_string(binary[i], binary[j]) + if k != -1: + check1[i] = "*" + check1[j] = "*" + temp.append(k) + for i in range(len(binary)): + if check1[i] == "$": + pi.append(binary[i]) + if len(temp) == 0: + return pi + binary = list(set(temp)) + + +def decimal_to_binary(no_of_variable, minterms): + """ + >>> decimal_to_binary(3,[1.5]) + ['0.00.01.5'] + """ + temp = [] + s = "" + for m in minterms: + for i in range(no_of_variable): + s = str(m % 2) + s + m //= 2 + temp.append(s) + s = "" + return temp + + +def is_for_table(string1, string2, count): + """ + >>> is_for_table('__1','011',2) + True + + >>> is_for_table('01_','001',1) + False + """ + l1 = list(string1) + l2 = list(string2) + count_n = 0 + for i in range(len(l1)): + if l1[i] != l2[i]: + count_n += 1 + if count_n == count: + return True + else: + return False + + +def selection(chart, prime_implicants): + """ + >>> selection([[1]],['0.00.01.5']) + ['0.00.01.5'] + + >>> selection([[1]],['0.00.01.5']) + ['0.00.01.5'] + """ + temp = [] + select = [0] * len(chart) + for i in range(len(chart[0])): + count = 0 + rem = -1 + for j in range(len(chart)): + if chart[j][i] == 1: + count += 1 + rem = j + if count == 1: + select[rem] = 1 + for i in range(len(select)): + if select[i] == 1: + for j in range(len(chart[0])): + if chart[i][j] == 1: + for k in range(len(chart)): + chart[k][j] = 0 + temp.append(prime_implicants[i]) + while 1: + max_n = 0 + rem = -1 + count_n = 0 + for i in range(len(chart)): + count_n = chart[i].count(1) + if count_n > max_n: + max_n = count_n + rem = i + + if max_n == 0: + return temp + + temp.append(prime_implicants[rem]) + + for i in range(len(chart[0])): + if chart[rem][i] == 1: + for j in range(len(chart)): + chart[j][i] = 0 + + +def prime_implicant_chart(prime_implicants, binary): + """ + >>> prime_implicant_chart(['0.00.01.5'],['0.00.01.5']) + [[1]] + """ + chart = [[0 for x in range(len(binary))] for x in range(len(prime_implicants))] + for i in range(len(prime_implicants)): + count = prime_implicants[i].count("_") + for j in range(len(binary)): + if is_for_table(prime_implicants[i], binary[j], count): + chart[i][j] = 1 + + return chart + + +def main(): + no_of_variable = int(input("Enter the no. of variables\n")) + minterms = [ + int(x) + for x in input( + "Enter the decimal representation of Minterms 'Spaces Separated'\n" + ).split() + ] + binary = decimal_to_binary(no_of_variable, minterms) + + prime_implicants = check(binary) + print("Prime Implicants are:") + print(prime_implicants) + chart = prime_implicant_chart(prime_implicants, binary) + + essential_prime_implicants = selection(chart, prime_implicants) + print("Essential Prime Implicants are:") + print(essential_prime_implicants) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() diff --git a/cellular_automata/one_dimensional.py b/cellular_automata/one_dimensional.py index a6229dd9096f..da77e444502f 100644 --- a/cellular_automata/one_dimensional.py +++ b/cellular_automata/one_dimensional.py @@ -4,7 +4,7 @@ https://mathworld.wolfram.com/ElementaryCellularAutomaton.html """ -from typing import List +from __future__ import annotations from PIL import Image @@ -15,7 +15,7 @@ # fmt: on -def format_ruleset(ruleset: int) -> List[int]: +def format_ruleset(ruleset: int) -> list[int]: """ >>> format_ruleset(11100) [0, 0, 0, 1, 1, 1, 0, 0] @@ -27,7 +27,7 @@ def format_ruleset(ruleset: int) -> List[int]: return [int(c) for c in f"{ruleset:08}"[:8]] -def new_generation(cells: List[List[int]], rule: List[int], time: int) -> List[int]: +def new_generation(cells: list[list[int]], rule: list[int], time: int) -> list[int]: population = len(cells[0]) # 31 next_generation = [] for i in range(population): @@ -41,7 +41,7 @@ def new_generation(cells: List[List[int]], rule: List[int], time: int) -> List[i return next_generation -def generate_image(cells: List[List[int]]) -> Image.Image: +def generate_image(cells: list[list[int]]) -> Image.Image: """ Convert the cells into a greyscale PIL.Image.Image and return it to the caller. >>> from random import random diff --git a/ciphers/morse_code_implementation.py b/ciphers/morse_code_implementation.py index 6df4632af4cb..04af8fcf6fdf 100644 --- a/ciphers/morse_code_implementation.py +++ b/ciphers/morse_code_implementation.py @@ -1,6 +1,5 @@ # Python program to implement Morse Code Translator - # Dictionary representing the morse code chart MORSE_CODE_DICT = { "A": ".-", @@ -39,13 +38,22 @@ "8": "---..", "9": "----.", "0": "-----", - ", ": "--..--", + "&": ".-...", + "@": ".--.-.", + ":": "---...", + ",": "--..--", ".": ".-.-.-", + "'": ".----.", + '"': ".-..-.", "?": "..--..", "/": "-..-.", + "=": "-...-", + "+": ".-.-.", "-": "-....-", "(": "-.--.", ")": "-.--.-", + # Exclamation mark is not in ITU-R recommendation + "!": "-.-.--", } @@ -53,42 +61,24 @@ def encrypt(message): cipher = "" for letter in message: if letter != " ": - cipher += MORSE_CODE_DICT[letter] + " " else: + cipher += "/ " - cipher += " " - - return cipher + # Remove trailing space added on line 64 + return cipher[:-1] def decrypt(message): - - message += " " - decipher = "" - citext = "" - for letter in message: - - if letter != " ": - - i = 0 - - citext += letter - + letters = message.split(" ") + for letter in letters: + if letter != "/": + decipher += list(MORSE_CODE_DICT.keys())[ + list(MORSE_CODE_DICT.values()).index(letter) + ] else: - - i += 1 - - if i == 2: - - decipher += " " - else: - - decipher += list(MORSE_CODE_DICT.keys())[ - list(MORSE_CODE_DICT.values()).index(citext) - ] - citext = "" + decipher += " " return decipher diff --git a/ciphers/rsa_factorization.py b/ciphers/rsa_factorization.py index 9ec34e6c5a17..6df32b6cc887 100644 --- a/ciphers/rsa_factorization.py +++ b/ciphers/rsa_factorization.py @@ -7,12 +7,13 @@ More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html large number can take minutes to factor, therefore are not included in doctest. """ +from __future__ import annotations + import math import random -from typing import List -def rsafactor(d: int, e: int, N: int) -> List[int]: +def rsafactor(d: int, e: int, N: int) -> list[int]: """ This function returns the factors of N, where p*q=N Return: [p, q] diff --git a/compression/burrows_wheeler.py b/compression/burrows_wheeler.py index 03912f80e1a7..1a6610915e65 100644 --- a/compression/burrows_wheeler.py +++ b/compression/burrows_wheeler.py @@ -10,10 +10,10 @@ original character. The BWT is thus a "free" method of improving the efficiency of text compression algorithms, costing only some extra computation. """ -from typing import Dict, List +from __future__ import annotations -def all_rotations(s: str) -> List[str]: +def all_rotations(s: str) -> list[str]: """ :param s: The string that will be rotated len(s) times. :return: A list with the rotations. @@ -43,7 +43,7 @@ def all_rotations(s: str) -> List[str]: return [s[i:] + s[:i] for i in range(len(s))] -def bwt_transform(s: str) -> Dict: +def bwt_transform(s: str) -> dict: """ :param s: The string that will be used at bwt algorithm :return: the string composed of the last char of each row of the ordered diff --git a/conversions/binary_to_octal.py b/conversions/binary_to_octal.py new file mode 100644 index 000000000000..8b594887867e --- /dev/null +++ b/conversions/binary_to_octal.py @@ -0,0 +1,45 @@ +""" +The function below will convert any binary string to the octal equivalent. + +>>> bin_to_octal("1111") +'17' + +>>> bin_to_octal("101010101010011") +'52523' + +>>> bin_to_octal("") +Traceback (most recent call last): +... +ValueError: Empty string was passed to the function +>>> bin_to_octal("a-1") +Traceback (most recent call last): +... +ValueError: Non-binary value was passed to the function +""" + + +def bin_to_octal(bin_string: str) -> str: + if not all(char in "01" for char in bin_string): + raise ValueError("Non-binary value was passed to the function") + if not bin_string: + raise ValueError("Empty string was passed to the function") + oct_string = "" + while len(bin_string) % 3 != 0: + bin_string = "0" + bin_string + bin_string_in_3_list = [ + bin_string[index : index + 3] + for index, value in enumerate(bin_string) + if index % 3 == 0 + ] + for bin_group in bin_string_in_3_list: + oct_val = 0 + for index, val in enumerate(bin_group): + oct_val += int(2 ** (2 - index) * int(val)) + oct_string += str(oct_val) + return oct_string + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/data_structures/binary_tree/lazy_segment_tree.py b/data_structures/binary_tree/lazy_segment_tree.py index 38d93a32e767..5bc79e74efcd 100644 --- a/data_structures/binary_tree/lazy_segment_tree.py +++ b/data_structures/binary_tree/lazy_segment_tree.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import math -from typing import List class SegmentTree: @@ -36,7 +37,7 @@ def right(self, idx: int) -> int: return idx * 2 + 1 def build( - self, idx: int, left_element: int, right_element: int, A: List[int] + self, idx: int, left_element: int, right_element: int, A: list[int] ) -> None: if left_element == right_element: self.segment_tree[idx] = A[left_element - 1] diff --git a/data_structures/binary_tree/lowest_common_ancestor.py b/data_structures/binary_tree/lowest_common_ancestor.py index c25536cdaef0..2f1e893fcf99 100644 --- a/data_structures/binary_tree/lowest_common_ancestor.py +++ b/data_structures/binary_tree/lowest_common_ancestor.py @@ -1,11 +1,12 @@ # https://en.wikipedia.org/wiki/Lowest_common_ancestor # https://en.wikipedia.org/wiki/Breadth-first_search +from __future__ import annotations + import queue -from typing import Dict, List, Tuple -def swap(a: int, b: int) -> Tuple[int, int]: +def swap(a: int, b: int) -> tuple[int, int]: """ Return a tuple (b, a) when given two integers a and b >>> swap(2,3) @@ -21,7 +22,7 @@ def swap(a: int, b: int) -> Tuple[int, int]: return a, b -def create_sparse(max_node: int, parent: List[List[int]]) -> List[List[int]]: +def create_sparse(max_node: int, parent: list[list[int]]) -> list[list[int]]: """ creating sparse table which saves each nodes 2^i-th parent """ @@ -35,8 +36,8 @@ def create_sparse(max_node: int, parent: List[List[int]]) -> List[List[int]]: # returns lca of node u,v def lowest_common_ancestor( - u: int, v: int, level: List[int], parent: List[List[int]] -) -> List[List[int]]: + u: int, v: int, level: list[int], parent: list[list[int]] +) -> list[list[int]]: # u must be deeper in the tree than v if level[u] < level[v]: u, v = swap(u, v) @@ -57,12 +58,12 @@ def lowest_common_ancestor( # runs a breadth first search from root node of the tree def breadth_first_search( - level: List[int], - parent: List[List[int]], + level: list[int], + parent: list[list[int]], max_node: int, - graph: Dict[int, int], + graph: dict[int, int], root=1, -) -> Tuple[List[int], List[List[int]]]: +) -> tuple[list[int], list[list[int]]]: """ sets every nodes direct parent parent of root node is set to 0 diff --git a/data_structures/binary_tree/non_recursive_segment_tree.py b/data_structures/binary_tree/non_recursive_segment_tree.py index cdcf1fa8dd2d..064e5aded7b4 100644 --- a/data_structures/binary_tree/non_recursive_segment_tree.py +++ b/data_structures/binary_tree/non_recursive_segment_tree.py @@ -35,13 +35,15 @@ >>> st.query(0, 2) [1, 2, 3] """ -from typing import Callable, List, TypeVar +from __future__ import annotations + +from typing import Callable, TypeVar T = TypeVar("T") class SegmentTree: - def __init__(self, arr: List[T], fnc: Callable[[T, T], T]) -> None: + def __init__(self, arr: list[T], fnc: Callable[[T, T], T]) -> None: """ Segment Tree constructor, it works just with commutative combiner. :param arr: list of elements for the segment tree diff --git a/data_structures/binary_tree/treap.py b/data_structures/binary_tree/treap.py index fbb57650280e..26648f7aba61 100644 --- a/data_structures/binary_tree/treap.py +++ b/data_structures/binary_tree/treap.py @@ -1,7 +1,8 @@ # flake8: noqa +from __future__ import annotations + from random import random -from typing import Tuple class Node: @@ -33,7 +34,7 @@ def __str__(self): return value + left + right -def split(root: Node, value: int) -> Tuple[Node, Node]: +def split(root: Node, value: int) -> tuple[Node, Node]: """ We split current tree into 2 trees with value: diff --git a/data_structures/linked_list/singly_linked_list.py b/data_structures/linked_list/singly_linked_list.py index 1f3e03a31b7e..e45a210a1785 100644 --- a/data_structures/linked_list/singly_linked_list.py +++ b/data_structures/linked_list/singly_linked_list.py @@ -9,50 +9,139 @@ def __repr__(self): class LinkedList: def __init__(self): - self.head = None # initialize head to None + self.head = None + + def __iter__(self): + node = self.head + while node: + yield node.data + node = node.next + + def __len__(self) -> int: + """ + Return length of linked list i.e. number of nodes + >>> linked_list = LinkedList() + >>> len(linked_list) + 0 + >>> linked_list.insert_tail("head") + >>> len(linked_list) + 1 + >>> linked_list.insert_head("head") + >>> len(linked_list) + 2 + >>> _ = linked_list.delete_tail() + >>> len(linked_list) + 1 + >>> _ = linked_list.delete_head() + >>> len(linked_list) + 0 + """ + return len(tuple(iter(self))) + + def __repr__(self): + """ + String representation/visualization of a Linked Lists + """ + return "->".join([str(item) for item in self]) + + def __getitem__(self, index): + """ + Indexing Support. Used to get a node at particular position + >>> linked_list = LinkedList() + >>> for i in range(0, 10): + ... linked_list.insert_nth(i, i) + >>> all(str(linked_list[i]) == str(i) for i in range(0, 10)) + True + >>> linked_list[-10] + Traceback (most recent call last): + ... + ValueError: list index out of range. + >>> linked_list[len(linked_list)] + Traceback (most recent call last): + ... + ValueError: list index out of range. + """ + if not 0 <= index < len(self): + raise ValueError("list index out of range.") + for i, node in enumerate(self): + if i == index: + return node + + # Used to change the data of a particular node + def __setitem__(self, index, data): + """ + >>> linked_list = LinkedList() + >>> for i in range(0, 10): + ... linked_list.insert_nth(i, i) + >>> linked_list[0] = 666 + >>> linked_list[0] + 666 + >>> linked_list[5] = -666 + >>> linked_list[5] + -666 + >>> linked_list[-10] = 666 + Traceback (most recent call last): + ... + ValueError: list index out of range. + >>> linked_list[len(linked_list)] = 666 + Traceback (most recent call last): + ... + ValueError: list index out of range. + """ + if not 0 <= index < len(self): + raise ValueError("list index out of range.") + current = self.head + for i in range(index): + current = current.next + current.data = data def insert_tail(self, data) -> None: + self.insert_nth(len(self), data) + + def insert_head(self, data) -> None: + self.insert_nth(0, data) + + def insert_nth(self, index: int, data) -> None: + if not 0 <= index <= len(self): + raise IndexError("list index out of range") + new_node = Node(data) if self.head is None: - self.insert_head(data) # if this is first node, call insert_head + self.head = new_node + elif index == 0: + new_node.next = self.head # link new_node to head + self.head = new_node else: temp = self.head - while temp.next: # traverse to last node + for _ in range(index - 1): temp = temp.next - temp.next = Node(data) # create node & link to tail - - def insert_head(self, data) -> None: - new_node = Node(data) # create a new node - if self.head: - new_node.next = self.head # link new_node to head - self.head = new_node # make NewNode as head + new_node.next = temp.next + temp.next = new_node def print_list(self) -> None: # print every node data - temp = self.head - while temp: - print(temp.data) - temp = temp.next - - def delete_head(self): # delete from head - temp = self.head - if self.head: - self.head = self.head.next - temp.next = None - return temp + print(self) + + def delete_head(self): + return self.delete_nth(0) def delete_tail(self): # delete from tail - temp = self.head - if self.head: - if self.head.next is None: # if head is the only Node in the Linked List - self.head = None - else: - while temp.next.next: # find the 2nd last element - temp = temp.next - # (2nd last element).next = None and temp = last element - temp.next, temp = None, temp.next - return temp + return self.delete_nth(len(self) - 1) + + def delete_nth(self, index: int = 0): + if not 0 <= index <= len(self) - 1: # test if index is valid + raise IndexError("list index out of range") + delete_node = self.head # default first node + if index == 0: + self.head = self.head.next + else: + temp = self.head + for _ in range(index - 1): + temp = temp.next + delete_node = temp.next + temp.next = temp.next.next + return delete_node.data def is_empty(self) -> bool: - return self.head is None # return True if head is none + return self.head is None def reverse(self): prev = None @@ -70,101 +159,81 @@ def reverse(self): # Return prev in order to put the head at the end self.head = prev - def __repr__(self): # String representation/visualization of a Linked Lists - current = self.head - string_repr = "" - while current: - string_repr += f"{current} --> " - current = current.next - # END represents end of the LinkedList - return string_repr + "END" - # Indexing Support. Used to get a node at particular position - def __getitem__(self, index): - current = self.head +def test_singly_linked_list() -> None: + """ + >>> test_singly_linked_list() + """ + linked_list = LinkedList() + assert linked_list.is_empty() is True + assert str(linked_list) == "" - # If LinkedList is empty - if current is None: - raise IndexError("The Linked List is empty") + try: + linked_list.delete_head() + assert False # This should not happen. + except IndexError: + assert True # This should happen. - # Move Forward 'index' times - for _ in range(index): - # If the LinkedList ends before reaching specified node - if current.next is None: - raise IndexError("Index out of range.") - current = current.next - return current + try: + linked_list.delete_tail() + assert False # This should not happen. + except IndexError: + assert True # This should happen. - # Used to change the data of a particular node - def __setitem__(self, index, data): - current = self.head - # If list is empty - if current is None: - raise IndexError("The Linked List is empty") - for i in range(index): - if current.next is None: - raise IndexError("Index out of range.") - current = current.next - current.data = data + for i in range(10): + assert len(linked_list) == i + linked_list.insert_nth(i, i + 1) + assert str(linked_list) == "->".join(str(i) for i in range(1, 11)) - def __len__(self): - """ - Return length of linked list i.e. number of nodes - >>> linked_list = LinkedList() - >>> len(linked_list) - 0 - >>> linked_list.insert_tail("head") - >>> len(linked_list) - 1 - >>> linked_list.insert_head("head") - >>> len(linked_list) - 2 - >>> _ = linked_list.delete_tail() - >>> len(linked_list) - 1 - >>> _ = linked_list.delete_head() - >>> len(linked_list) - 0 - """ - if not self.head: - return 0 + linked_list.insert_head(0) + linked_list.insert_tail(11) + assert str(linked_list) == "->".join(str(i) for i in range(0, 12)) + + assert linked_list.delete_head() == 0 + assert linked_list.delete_nth(9) == 10 + assert linked_list.delete_tail() == 11 + assert len(linked_list) == 9 + assert str(linked_list) == "->".join(str(i) for i in range(1, 10)) + + assert all(linked_list[i] == i + 1 for i in range(0, 9)) is True - count = 0 - cur_node = self.head - while cur_node.next: - count += 1 - cur_node = cur_node.next - return count + 1 + for i in range(0, 9): + linked_list[i] = -i + assert all(linked_list[i] == -i for i in range(0, 9)) is True def main(): - A = LinkedList() - A.insert_head(input("Inserting 1st at head ").strip()) - A.insert_head(input("Inserting 2nd at head ").strip()) + from doctest import testmod + + testmod() + + linked_list = LinkedList() + linked_list.insert_head(input("Inserting 1st at head ").strip()) + linked_list.insert_head(input("Inserting 2nd at head ").strip()) print("\nPrint list:") - A.print_list() - A.insert_tail(input("\nInserting 1st at tail ").strip()) - A.insert_tail(input("Inserting 2nd at tail ").strip()) + linked_list.print_list() + linked_list.insert_tail(input("\nInserting 1st at tail ").strip()) + linked_list.insert_tail(input("Inserting 2nd at tail ").strip()) print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nDelete head") - A.delete_head() + linked_list.delete_head() print("Delete tail") - A.delete_tail() + linked_list.delete_tail() print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nReverse linked list") - A.reverse() + linked_list.reverse() print("\nPrint list:") - A.print_list() + linked_list.print_list() print("\nString representation of linked list:") - print(A) + print(linked_list) print("\nReading/changing Node data using indexing:") - print(f"Element at Position 1: {A[1]}") - A[1] = input("Enter New Value: ").strip() + print(f"Element at Position 1: {linked_list[1]}") + linked_list[1] = input("Enter New Value: ").strip() print("New list:") - print(A) - print(f"length of A is : {len(A)}") + print(linked_list) + print(f"length of linked_list is : {len(linked_list)}") if __name__ == "__main__": diff --git a/data_structures/linked_list/skip_list.py b/data_structures/linked_list/skip_list.py index ee572cd3ed19..8f06e6193d52 100644 --- a/data_structures/linked_list/skip_list.py +++ b/data_structures/linked_list/skip_list.py @@ -3,8 +3,10 @@ https://epaperpress.com/sortsearch/download/skiplist.pdf """ +from __future__ import annotations + from random import random -from typing import Generic, List, Optional, Tuple, TypeVar +from typing import Generic, Optional, TypeVar KT = TypeVar("KT") VT = TypeVar("VT") @@ -14,7 +16,7 @@ class Node(Generic[KT, VT]): def __init__(self, key: KT, value: VT): self.key = key self.value = value - self.forward: List[Node[KT, VT]] = [] + self.forward: list[Node[KT, VT]] = [] def __repr__(self) -> str: """ @@ -122,7 +124,7 @@ def random_level(self) -> int: return level - def _locate_node(self, key) -> Tuple[Optional[Node[KT, VT]], List[Node[KT, VT]]]: + def _locate_node(self, key) -> tuple[Optional[Node[KT, VT]], list[Node[KT, VT]]]: """ :param key: Searched key, :return: Tuple with searched node (or None if given key is not present) diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py index 486258e8bae0..29a174daebf9 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import math -from typing import List, Tuple -def default_matrix_multiplication(a: List, b: List) -> List: +def default_matrix_multiplication(a: list, b: list) -> list: """ Multiplication only for 2x2 matrices """ @@ -15,23 +16,21 @@ def default_matrix_multiplication(a: List, b: List) -> List: return new_matrix -def matrix_addition(matrix_a: List, matrix_b: List): +def matrix_addition(matrix_a: list, matrix_b: list): return [ [matrix_a[row][col] + matrix_b[row][col] for col in range(len(matrix_a[row]))] for row in range(len(matrix_a)) ] -def matrix_subtraction(matrix_a: List, matrix_b: List): +def matrix_subtraction(matrix_a: list, matrix_b: list): return [ [matrix_a[row][col] - matrix_b[row][col] for col in range(len(matrix_a[row]))] for row in range(len(matrix_a)) ] -def split_matrix( - a: List, -) -> Tuple[List, List, List, List]: +def split_matrix(a: list) -> tuple[list, list, list, list]: """ Given an even length matrix, returns the top_left, top_right, bot_left, bot_right quadrant. @@ -64,16 +63,16 @@ def split_matrix( return top_left, top_right, bot_left, bot_right -def matrix_dimensions(matrix: List) -> Tuple[int, int]: +def matrix_dimensions(matrix: list) -> tuple[int, int]: return len(matrix), len(matrix[0]) -def print_matrix(matrix: List) -> None: +def print_matrix(matrix: list) -> None: for i in range(len(matrix)): print(matrix[i]) -def actual_strassen(matrix_a: List, matrix_b: List) -> List: +def actual_strassen(matrix_a: list, matrix_b: list) -> list: """ Recursive function to calculate the product of two matrices, using the Strassen Algorithm. It only supports even length matrices. @@ -106,7 +105,7 @@ def actual_strassen(matrix_a: List, matrix_b: List) -> List: return new_matrix -def strassen(matrix1: List, matrix2: List) -> List: +def strassen(matrix1: list, matrix2: list) -> list: """ >>> strassen([[2,1,3],[3,4,6],[1,4,2],[7,6,7]], [[4,2,3,4],[2,1,1,1],[8,6,4,2]]) [[34, 23, 19, 15], [68, 46, 37, 28], [28, 18, 15, 12], [96, 62, 55, 48]] diff --git a/dynamic_programming/fast_fibonacci.py b/dynamic_programming/fast_fibonacci.py index 63481fe70a92..f48186a34c25 100644 --- a/dynamic_programming/fast_fibonacci.py +++ b/dynamic_programming/fast_fibonacci.py @@ -4,8 +4,9 @@ This program calculates the nth Fibonacci number in O(log(n)). It's possible to calculate F(1_000_000) in less than a second. """ +from __future__ import annotations + import sys -from typing import Tuple def fibonacci(n: int) -> int: @@ -20,7 +21,7 @@ def fibonacci(n: int) -> int: # returns (F(n), F(n-1)) -def _fib(n: int) -> Tuple[int, int]: +def _fib(n: int) -> tuple[int, int]: if n == 0: # (F(0), F(1)) return (0, 1) diff --git a/dynamic_programming/fractional_knapsack_2.py b/dynamic_programming/fractional_knapsack_2.py index eadb73c61a39..cae57738311b 100644 --- a/dynamic_programming/fractional_knapsack_2.py +++ b/dynamic_programming/fractional_knapsack_2.py @@ -2,12 +2,12 @@ # https://www.guru99.com/fractional-knapsack-problem-greedy.html # https://medium.com/walkinthecode/greedy-algorithm-fractional-knapsack-problem-9aba1daecc93 -from typing import List, Tuple +from __future__ import annotations def fractional_knapsack( - value: List[int], weight: List[int], capacity: int -) -> Tuple[int, List[int]]: + value: list[int], weight: list[int], capacity: int +) -> tuple[int, list[int]]: """ >>> value = [1, 3, 5, 7, 9] >>> weight = [0.9, 0.7, 0.5, 0.3, 0.1] diff --git a/dynamic_programming/iterating_through_submasks.py b/dynamic_programming/iterating_through_submasks.py index cb27a5b884bd..855af61d6707 100644 --- a/dynamic_programming/iterating_through_submasks.py +++ b/dynamic_programming/iterating_through_submasks.py @@ -5,10 +5,10 @@ its submasks. The mask s is submask of m if only bits that were included in bitmask are set """ -from typing import List +from __future__ import annotations -def list_of_submasks(mask: int) -> List[int]: +def list_of_submasks(mask: int) -> list[int]: """ Args: diff --git a/dynamic_programming/longest_increasing_subsequence.py b/dynamic_programming/longest_increasing_subsequence.py index 48d5e8e8fade..f5ca8a2b5cdc 100644 --- a/dynamic_programming/longest_increasing_subsequence.py +++ b/dynamic_programming/longest_increasing_subsequence.py @@ -10,10 +10,10 @@ Example: [10, 22, 9, 33, 21, 50, 41, 60, 80] as input will return [10, 22, 33, 41, 60, 80] as output """ -from typing import List +from __future__ import annotations -def longest_subsequence(array: List[int]) -> List[int]: # This function is recursive +def longest_subsequence(array: list[int]) -> list[int]: # This function is recursive """ Some examples >>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) diff --git a/dynamic_programming/longest_increasing_subsequence_o(nlogn).py b/dynamic_programming/longest_increasing_subsequence_o(nlogn).py index b33774057db3..af536f8bbd01 100644 --- a/dynamic_programming/longest_increasing_subsequence_o(nlogn).py +++ b/dynamic_programming/longest_increasing_subsequence_o(nlogn).py @@ -4,7 +4,7 @@ # comments: This programme outputs the Longest Strictly Increasing Subsequence in # O(NLogN) Where N is the Number of elements in the list ############################# -from typing import List +from __future__ import annotations def CeilIndex(v, l, r, key): # noqa: E741 @@ -17,7 +17,7 @@ def CeilIndex(v, l, r, key): # noqa: E741 return r -def LongestIncreasingSubsequenceLength(v: List[int]) -> int: +def LongestIncreasingSubsequenceLength(v: list[int]) -> int: """ >>> LongestIncreasingSubsequenceLength([2, 5, 3, 7, 11, 8, 10, 13, 6]) 6 diff --git a/dynamic_programming/max_non_adjacent_sum.py b/dynamic_programming/max_non_adjacent_sum.py index 15dd8ce664ed..5362b22ca9dc 100644 --- a/dynamic_programming/max_non_adjacent_sum.py +++ b/dynamic_programming/max_non_adjacent_sum.py @@ -1,9 +1,9 @@ # Video Explanation: https://www.youtube.com/watch?v=6w60Zi1NtL8&feature=emb_logo -from typing import List +from __future__ import annotations -def maximum_non_adjacent_sum(nums: List[int]) -> int: +def maximum_non_adjacent_sum(nums: list[int]) -> int: """ Find the maximum non-adjacent sum of the integers in the nums input list diff --git a/dynamic_programming/max_sub_array.py b/dynamic_programming/max_sub_array.py index 1ca4f90bbaa2..3060010ef7c6 100644 --- a/dynamic_programming/max_sub_array.py +++ b/dynamic_programming/max_sub_array.py @@ -1,7 +1,7 @@ """ author : Mayank Kumar Jha (mk9440) """ -from typing import List +from __future__ import annotations def find_max_sub_array(A, low, high): @@ -38,7 +38,7 @@ def find_max_cross_sum(A, low, mid, high): return max_left, max_right, (left_sum + right_sum) -def max_sub_array(nums: List[int]) -> int: +def max_sub_array(nums: list[int]) -> int: """ Finds the contiguous subarray which has the largest sum and return its sum. diff --git a/dynamic_programming/minimum_cost_path.py b/dynamic_programming/minimum_cost_path.py index 09295a4fafbe..3ad24b5528d1 100644 --- a/dynamic_programming/minimum_cost_path.py +++ b/dynamic_programming/minimum_cost_path.py @@ -1,9 +1,9 @@ # Youtube Explanation: https://www.youtube.com/watch?v=lBRtnuxg-gU -from typing import List +from __future__ import annotations -def minimum_cost_path(matrix: List[List[int]]) -> int: +def minimum_cost_path(matrix: list[list[int]]) -> int: """ Find the minimum cost traced by all possible paths from top left to bottom right in a given matrix diff --git a/genetic_algorithm/basic_string.py b/genetic_algorithm/basic_string.py index 482a6cb5e656..97dbe182bc82 100644 --- a/genetic_algorithm/basic_string.py +++ b/genetic_algorithm/basic_string.py @@ -5,8 +5,9 @@ Author: D4rkia """ +from __future__ import annotations + import random -from typing import List, Tuple # Maximum size of the population. bigger could be faster but is more memory expensive N_POPULATION = 200 @@ -20,7 +21,7 @@ random.seed(random.randint(0, 1000)) -def basic(target: str, genes: List[str], debug: bool = True) -> Tuple[int, int, str]: +def basic(target: str, genes: list[str], debug: bool = True) -> tuple[int, int, str]: """ Verify that the target contains no genes besides the ones inside genes variable. @@ -69,7 +70,7 @@ def basic(target: str, genes: List[str], debug: bool = True) -> Tuple[int, int, total_population += len(population) # Random population created now it's time to evaluate - def evaluate(item: str, main_target: str = target) -> Tuple[str, float]: + def evaluate(item: str, main_target: str = target) -> tuple[str, float]: """ Evaluate how similar the item is with the target by just counting each char in the right position @@ -84,7 +85,7 @@ def evaluate(item: str, main_target: str = target) -> Tuple[str, float]: # Adding a bit of concurrency can make everything faster, # # import concurrent.futures - # population_score: List[Tuple[str, float]] = [] + # population_score: list[tuple[str, float]] = [] # with concurrent.futures.ThreadPoolExecutor( # max_workers=NUM_WORKERS) as executor: # futures = {executor.submit(evaluate, item) for item in population} @@ -121,7 +122,7 @@ def evaluate(item: str, main_target: str = target) -> Tuple[str, float]: ] # Select, Crossover and Mutate a new population - def select(parent_1: Tuple[str, float]) -> List[str]: + def select(parent_1: tuple[str, float]) -> list[str]: """Select the second parent and generate new population""" pop = [] # Generate more child proportionally to the fitness score @@ -135,7 +136,7 @@ def select(parent_1: Tuple[str, float]) -> List[str]: pop.append(mutate(child_2)) return pop - def crossover(parent_1: str, parent_2: str) -> Tuple[str, str]: + def crossover(parent_1: str, parent_2: str) -> tuple[str, str]: """Slice and combine two string in a random point""" random_slice = random.randint(0, len(parent_1) - 1) child_1 = parent_1[:random_slice] + parent_2[random_slice:] diff --git a/graphics/bezier_curve.py b/graphics/bezier_curve.py index 48755647e02d..295ff47e8cdc 100644 --- a/graphics/bezier_curve.py +++ b/graphics/bezier_curve.py @@ -1,6 +1,6 @@ # https://en.wikipedia.org/wiki/B%C3%A9zier_curve # https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm -from typing import List, Tuple +from __future__ import annotations from scipy.special import comb @@ -12,7 +12,7 @@ class BezierCurve: This implementation works only for 2d coordinates in the xy plane. """ - def __init__(self, list_of_points: List[Tuple[float, float]]): + def __init__(self, list_of_points: list[tuple[float, float]]): """ list_of_points: Control points in the xy plane on which to interpolate. These points control the behavior (shape) of the Bezier curve. @@ -22,7 +22,7 @@ def __init__(self, list_of_points: List[Tuple[float, float]]): # Degree = 1 will produce a straight line. self.degree = len(list_of_points) - 1 - def basis_function(self, t: float) -> List[float]: + def basis_function(self, t: float) -> list[float]: """ The basis function determines the weight of each control point at time t. t: time value between 0 and 1 inclusive at which to evaluate the basis of @@ -36,7 +36,7 @@ def basis_function(self, t: float) -> List[float]: [0.0, 1.0] """ assert 0 <= t <= 1, "Time t must be between 0 and 1." - output_values: List[float] = [] + output_values: list[float] = [] for i in range(len(self.list_of_points)): # basis function for each i output_values.append( @@ -46,7 +46,7 @@ def basis_function(self, t: float) -> List[float]: assert round(sum(output_values), 5) == 1 return output_values - def bezier_curve_function(self, t: float) -> Tuple[float, float]: + def bezier_curve_function(self, t: float) -> tuple[float, float]: """ The function to produce the values of the Bezier curve at time t. t: the value of time t at which to evaluate the Bezier function @@ -80,8 +80,8 @@ def plot_curve(self, step_size: float = 0.01): """ from matplotlib import pyplot as plt - to_plot_x: List[float] = [] # x coordinates of points to plot - to_plot_y: List[float] = [] # y coordinates of points to plot + to_plot_x: list[float] = [] # x coordinates of points to plot + to_plot_y: list[float] = [] # y coordinates of points to plot t = 0.0 while t <= 1: diff --git a/graphs/bellman_ford.py b/graphs/bellman_ford.py index d4d37a365e03..ace7985647bb 100644 --- a/graphs/bellman_ford.py +++ b/graphs/bellman_ford.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from __future__ import annotations def printDist(dist, V): @@ -7,7 +7,7 @@ def printDist(dist, V): print("\t".join(f"{i}\t{d}" for i, d in enumerate(distances))) -def BellmanFord(graph: List[Dict[str, int]], V: int, E: int, src: int) -> int: +def BellmanFord(graph: list[dict[str, int]], V: int, E: int, src: int) -> int: """ Returns shortest paths from a vertex src to all other vertices. diff --git a/graphs/bidirectional_a_star.py b/graphs/bidirectional_a_star.py index 76313af769be..72ff4fa65ff0 100644 --- a/graphs/bidirectional_a_star.py +++ b/graphs/bidirectional_a_star.py @@ -2,9 +2,10 @@ https://en.wikipedia.org/wiki/Bidirectional_search """ +from __future__ import annotations + import time from math import sqrt -from typing import List, Tuple # 1 for manhattan, 0 for euclidean HEURISTIC = 0 @@ -89,7 +90,7 @@ def __init__(self, start, goal): self.reached = False - def search(self) -> List[Tuple[int]]: + def search(self) -> list[tuple[int]]: while self.open_nodes: # Open Nodes are sorted using __lt__ self.open_nodes.sort() @@ -120,7 +121,7 @@ def search(self) -> List[Tuple[int]]: if not (self.reached): return [(self.start.pos)] - def get_successors(self, parent: Node) -> List[Node]: + def get_successors(self, parent: Node) -> list[Node]: """ Returns a list of successors (both in the grid and free spaces) """ @@ -146,7 +147,7 @@ def get_successors(self, parent: Node) -> List[Node]: ) return successors - def retrace_path(self, node: Node) -> List[Tuple[int]]: + def retrace_path(self, node: Node) -> list[tuple[int]]: """ Retrace the path from parents to parents until start node """ @@ -177,7 +178,7 @@ def __init__(self, start, goal): self.bwd_astar = AStar(goal, start) self.reached = False - def search(self) -> List[Tuple[int]]: + def search(self) -> list[tuple[int]]: while self.fwd_astar.open_nodes or self.bwd_astar.open_nodes: self.fwd_astar.open_nodes.sort() self.bwd_astar.open_nodes.sort() @@ -224,7 +225,7 @@ def search(self) -> List[Tuple[int]]: def retrace_bidirectional_path( self, fwd_node: Node, bwd_node: Node - ) -> List[Tuple[int]]: + ) -> list[tuple[int]]: fwd_path = self.fwd_astar.retrace_path(fwd_node) bwd_path = self.bwd_astar.retrace_path(bwd_node) bwd_path.pop() diff --git a/graphs/bidirectional_breadth_first_search.py b/graphs/bidirectional_breadth_first_search.py index d941c0db5893..39d8dc7d4187 100644 --- a/graphs/bidirectional_breadth_first_search.py +++ b/graphs/bidirectional_breadth_first_search.py @@ -2,8 +2,9 @@ https://en.wikipedia.org/wiki/Bidirectional_search """ +from __future__ import annotations + import time -from typing import List, Tuple grid = [ [0, 0, 0, 0, 0, 0, 0], @@ -51,7 +52,7 @@ def __init__(self, start, goal): self.node_queue = [self.start] self.reached = False - def search(self) -> List[Tuple[int]]: + def search(self) -> list[tuple[int]]: while self.node_queue: current_node = self.node_queue.pop(0) @@ -67,7 +68,7 @@ def search(self) -> List[Tuple[int]]: if not (self.reached): return [(self.start.pos)] - def get_successors(self, parent: Node) -> List[Node]: + def get_successors(self, parent: Node) -> list[Node]: """ Returns a list of successors (both in the grid and free spaces) """ @@ -86,7 +87,7 @@ def get_successors(self, parent: Node) -> List[Node]: ) return successors - def retrace_path(self, node: Node) -> List[Tuple[int]]: + def retrace_path(self, node: Node) -> list[tuple[int]]: """ Retrace the path from parents to parents until start node """ @@ -118,7 +119,7 @@ def __init__(self, start, goal): self.bwd_bfs = BreadthFirstSearch(goal, start) self.reached = False - def search(self) -> List[Tuple[int]]: + def search(self) -> list[tuple[int]]: while self.fwd_bfs.node_queue or self.bwd_bfs.node_queue: current_fwd_node = self.fwd_bfs.node_queue.pop(0) current_bwd_node = self.bwd_bfs.node_queue.pop(0) @@ -146,7 +147,7 @@ def search(self) -> List[Tuple[int]]: def retrace_bidirectional_path( self, fwd_node: Node, bwd_node: Node - ) -> List[Tuple[int]]: + ) -> list[tuple[int]]: fwd_path = self.fwd_bfs.retrace_path(fwd_node) bwd_path = self.bwd_bfs.retrace_path(bwd_node) bwd_path.pop() diff --git a/graphs/breadth_first_search_2.py b/graphs/breadth_first_search_2.py index 293a1012f61f..a90e963a4043 100644 --- a/graphs/breadth_first_search_2.py +++ b/graphs/breadth_first_search_2.py @@ -12,7 +12,7 @@ mark w as explored add w to Q (at the end) """ -from typing import Dict, Set +from __future__ import annotations G = { "A": ["B", "C"], @@ -24,7 +24,7 @@ } -def breadth_first_search(graph: Dict, start: str) -> Set[str]: +def breadth_first_search(graph: dict, start: str) -> set[str]: """ >>> ''.join(sorted(breadth_first_search(G, 'A'))) 'ABCDEF' diff --git a/graphs/breadth_first_search_shortest_path.py b/graphs/breadth_first_search_shortest_path.py index c25a5bb4f7dd..b43479d4659c 100644 --- a/graphs/breadth_first_search_shortest_path.py +++ b/graphs/breadth_first_search_shortest_path.py @@ -1,7 +1,7 @@ """Breath First Search (BFS) can be used when finding the shortest path from a given source node to a target node in an unweighted graph. """ -from typing import Dict +from __future__ import annotations graph = { "A": ["B", "C", "E"], @@ -15,7 +15,7 @@ class Graph: - def __init__(self, graph: Dict[str, str], source_vertex: str) -> None: + def __init__(self, graph: dict[str, str], source_vertex: str) -> None: """Graph is implemented as dictionary of adjacency lists. Also, Source vertex have to be defined upon initialization. """ diff --git a/graphs/depth_first_search.py b/graphs/depth_first_search.py index 43f2eaaea19a..907cc172f253 100644 --- a/graphs/depth_first_search.py +++ b/graphs/depth_first_search.py @@ -1,9 +1,9 @@ """Non recursive implementation of a DFS algorithm.""" -from typing import Dict, Set +from __future__ import annotations -def depth_first_search(graph: Dict, start: str) -> Set[int]: +def depth_first_search(graph: dict, start: str) -> set[int]: """Depth First Search on Graph :param graph: directed graph in dictionary format :param vertex: starting vertex as a string diff --git a/graphs/gale_shapley_bigraph.py b/graphs/gale_shapley_bigraph.py index 07a3922f86ec..59baf8296ea6 100644 --- a/graphs/gale_shapley_bigraph.py +++ b/graphs/gale_shapley_bigraph.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def stable_matching(donor_pref: List[int], recipient_pref: List[int]) -> List[int]: +def stable_matching(donor_pref: list[int], recipient_pref: list[int]) -> list[int]: """ Finds the stable match in any bipartite graph, i.e a pairing where no 2 objects prefer each other over their partner. The function accepts the preferences of diff --git a/graphs/greedy_best_first.py b/graphs/greedy_best_first.py index 2e63a50ce30a..4b80a6853d3f 100644 --- a/graphs/greedy_best_first.py +++ b/graphs/greedy_best_first.py @@ -2,7 +2,7 @@ https://en.wikipedia.org/wiki/Best-first_search#Greedy_BFS """ -from typing import List, Tuple +from __future__ import annotations grid = [ [0, 0, 0, 0, 0, 0, 0], @@ -81,7 +81,7 @@ def __init__(self, start, goal): self.reached = False - def search(self) -> List[Tuple[int]]: + def search(self) -> list[tuple[int]]: """ Search for the path, if a path is not found, only the starting position is returned @@ -116,7 +116,7 @@ def search(self) -> List[Tuple[int]]: if not (self.reached): return [self.start.pos] - def get_successors(self, parent: Node) -> List[Node]: + def get_successors(self, parent: Node) -> list[Node]: """ Returns a list of successors (both in the grid and free spaces) """ @@ -143,7 +143,7 @@ def get_successors(self, parent: Node) -> List[Node]: ) return successors - def retrace_path(self, node: Node) -> List[Tuple[int]]: + def retrace_path(self, node: Node) -> list[tuple[int]]: """ Retrace the path from parents to parents until start node """ diff --git a/graphs/karger.py b/graphs/karger.py index baa0eebd90b4..f72128c8178a 100644 --- a/graphs/karger.py +++ b/graphs/karger.py @@ -2,8 +2,9 @@ An implementation of Karger's Algorithm for partitioning a graph. """ +from __future__ import annotations + import random -from typing import Dict, List, Set, Tuple # Adjacency list representation of this graph: # https://en.wikipedia.org/wiki/File:Single_run_of_Karger%E2%80%99s_Mincut_algorithm.svg @@ -21,7 +22,7 @@ } -def partition_graph(graph: Dict[str, List[str]]) -> Set[Tuple[str, str]]: +def partition_graph(graph: dict[str, list[str]]) -> set[tuple[str, str]]: """ Partitions a graph using Karger's Algorithm. Implemented from pseudocode found here: @@ -60,9 +61,7 @@ def partition_graph(graph: Dict[str, List[str]]) -> Set[Tuple[str, str]]: for neighbor in uv_neighbors: graph_copy[neighbor].append(uv) - contracted_nodes[uv] = { - node for node in contracted_nodes[u].union(contracted_nodes[v]) - } + contracted_nodes[uv] = set(contracted_nodes[u].union(contracted_nodes[v])) # Remove nodes u and v. del graph_copy[u] diff --git a/graphs/prim.py b/graphs/prim.py index f7376cfb48ca..70329da7e8e2 100644 --- a/graphs/prim.py +++ b/graphs/prim.py @@ -100,7 +100,7 @@ def prim_heap(graph: list, root: Vertex) -> Iterator[tuple]: u.pi = None root.key = 0 - h = [v for v in graph] + h = list(graph) hq.heapify(h) while h: diff --git a/linear_algebra/src/polynom-for-points.py b/linear_algebra/src/polynom_for_points.py similarity index 94% rename from linear_algebra/src/polynom-for-points.py rename to linear_algebra/src/polynom_for_points.py index 8db89fc2c1ea..7a363723d9d2 100644 --- a/linear_algebra/src/polynom-for-points.py +++ b/linear_algebra/src/polynom_for_points.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def points_to_polynomial(coordinates: List[List[int]]) -> str: +def points_to_polynomial(coordinates: list[list[int]]) -> str: """ coordinates is a two dimensional matrix: [[x, y], [x, y], ...] number of points you want to use @@ -60,7 +60,7 @@ def points_to_polynomial(coordinates: List[List[int]]) -> str: while count_of_line < x: count_in_line = 0 a = coordinates[count_of_line][0] - count_line: List[int] = [] + count_line: list[int] = [] while count_in_line < x: count_line.append(a ** (x - (count_in_line + 1))) count_in_line += 1 @@ -69,7 +69,7 @@ def points_to_polynomial(coordinates: List[List[int]]) -> str: count_of_line = 0 # put the y values into a vector - vector: List[int] = [] + vector: list[int] = [] while count_of_line < x: vector.append(coordinates[count_of_line][1]) count_of_line += 1 @@ -94,7 +94,7 @@ def points_to_polynomial(coordinates: List[List[int]]) -> str: count = 0 # make solutions - solution: List[str] = [] + solution: list[str] = [] while count < x: solution.append(vector[count] / matrix[count][count]) count += 1 @@ -103,7 +103,7 @@ def points_to_polynomial(coordinates: List[List[int]]) -> str: solved = "f(x)=" while count < x: - remove_e: List[str] = str(solution[count]).split("E") + remove_e: list[str] = str(solution[count]).split("E") if len(remove_e) > 1: solution[count] = remove_e[0] + "*10^" + remove_e[1] solved += "x^" + str(x - (count + 1)) + "*" + str(solution[count]) diff --git a/linear_algebra/src/transformations_2d.py b/linear_algebra/src/transformations_2d.py index 9ee238fd7999..6a15189c5676 100644 --- a/linear_algebra/src/transformations_2d.py +++ b/linear_algebra/src/transformations_2d.py @@ -11,11 +11,12 @@ reflection(45) = [[0.05064397763545947, 0.893996663600558], [0.893996663600558, 0.7018070490682369]] """ +from __future__ import annotations + from math import cos, sin -from typing import List -def scaling(scaling_factor: float) -> List[List[float]]: +def scaling(scaling_factor: float) -> list[list[float]]: """ >>> scaling(5) [[5.0, 0.0], [0.0, 5.0]] @@ -24,7 +25,7 @@ def scaling(scaling_factor: float) -> List[List[float]]: return [[scaling_factor * int(x == y) for x in range(2)] for y in range(2)] -def rotation(angle: float) -> List[List[float]]: +def rotation(angle: float) -> list[list[float]]: """ >>> rotation(45) # doctest: +NORMALIZE_WHITESPACE [[0.5253219888177297, -0.8509035245341184], @@ -34,7 +35,7 @@ def rotation(angle: float) -> List[List[float]]: return [[c, -s], [s, c]] -def projection(angle: float) -> List[List[float]]: +def projection(angle: float) -> list[list[float]]: """ >>> projection(45) # doctest: +NORMALIZE_WHITESPACE [[0.27596319193541496, 0.446998331800279], @@ -45,7 +46,7 @@ def projection(angle: float) -> List[List[float]]: return [[c * c, cs], [cs, s * s]] -def reflection(angle: float) -> List[List[float]]: +def reflection(angle: float) -> list[list[float]]: """ >>> reflection(45) # doctest: +NORMALIZE_WHITESPACE [[0.05064397763545947, 0.893996663600558], diff --git a/maths/3n_plus_1.py b/maths/3n_plus_1.py index baaa74f89bf5..28c9fd7b426f 100644 --- a/maths/3n_plus_1.py +++ b/maths/3n_plus_1.py @@ -1,7 +1,7 @@ -from typing import List, Tuple +from __future__ import annotations -def n31(a: int) -> Tuple[List[int], int]: +def n31(a: int) -> tuple[list[int], int]: """ Returns the Collatz sequence and its length of any positive integer. >>> n31(4) diff --git a/maths/abs_max.py b/maths/abs_max.py index 554e27f6ee66..e5a8219657ac 100644 --- a/maths/abs_max.py +++ b/maths/abs_max.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def abs_max(x: List[int]) -> int: +def abs_max(x: list[int]) -> int: """ >>> abs_max([0,5,1,11]) 11 diff --git a/maths/allocation_number.py b/maths/allocation_number.py index c6f1e562f878..4e74bb2e6950 100644 --- a/maths/allocation_number.py +++ b/maths/allocation_number.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def allocation_num(number_of_bytes: int, partitions: int) -> List[str]: +def allocation_num(number_of_bytes: int, partitions: int) -> list[str]: """ Divide a number of bytes into x partitions. diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py index 6ace77312732..7b3636de69f4 100644 --- a/maths/collatz_sequence.py +++ b/maths/collatz_sequence.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def collatz_sequence(n: int) -> List[int]: +def collatz_sequence(n: int) -> list[int]: """ Collatz conjecture: start with any positive integer n. The next term is obtained as follows: diff --git a/maths/entropy.py b/maths/entropy.py index c380afd3b5c9..74980ef9e0c6 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -4,11 +4,11 @@ Implementation of entropy of information https://en.wikipedia.org/wiki/Entropy_(information_theory) """ +from __future__ import annotations import math from collections import Counter from string import ascii_lowercase -from typing import Tuple def calculate_prob(text: str) -> None: @@ -89,7 +89,7 @@ def calculate_prob(text: str) -> None: print("{0:.1f}".format(round(((-1 * my_sec_sum) - (-1 * my_fir_sum))))) -def analyze_text(text: str) -> Tuple[dict, dict]: +def analyze_text(text: str) -> tuple[dict, dict]: """ Convert text input into two dicts of counts. The first dictionary stores the frequency of single character strings. diff --git a/maths/is_square_free.py b/maths/is_square_free.py index 6d27d0af3387..8d83d95ffb67 100644 --- a/maths/is_square_free.py +++ b/maths/is_square_free.py @@ -3,10 +3,10 @@ python/black : True flake8 : True """ -from typing import List +from __future__ import annotations -def is_square_free(factors: List[int]) -> bool: +def is_square_free(factors: list[int]) -> bool: """ # doctest: +NORMALIZE_WHITESPACE This functions takes a list of prime factors as input. diff --git a/maths/line_length.py b/maths/line_length.py index 6df0a916efe6..1d386b44b50d 100644 --- a/maths/line_length.py +++ b/maths/line_length.py @@ -1,4 +1,4 @@ -import math as m +import math from typing import Callable, Union @@ -29,7 +29,7 @@ def line_length( '10.000000' >>> def f(x): - ... return m.sin(5 * x) + m.cos(10 * x) + x * x/10 + ... return math.sin(5 * x) + math.cos(10 * x) + x * x/10 >>> f"{line_length(f, 0.0, 10.0, 10000):.6f}" '69.534930' """ @@ -43,7 +43,7 @@ def line_length( # Approximates curve as a sequence of linear lines and sums their length x2 = (x_end - x_start) / steps + x1 fx2 = fnc(x2) - length += m.hypot(x2 - x1, fx2 - fx1) + length += math.hypot(x2 - x1, fx2 - fx1) # Increment step x1 = x2 @@ -55,7 +55,7 @@ def line_length( if __name__ == "__main__": def f(x): - return m.sin(10 * x) + return math.sin(10 * x) print("f(x) = sin(10 * x)") print("The length of the curve from x = -10 to x = 10 is:") diff --git a/maths/monte_carlo_dice.py b/maths/monte_carlo_dice.py index c36c3e83e00b..e8e3abe83a99 100644 --- a/maths/monte_carlo_dice.py +++ b/maths/monte_carlo_dice.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import random -from typing import List class Dice: @@ -16,7 +17,7 @@ def _str_(self): return "Fair Dice" -def throw_dice(num_throws: int, num_dice: int = 2) -> List[float]: +def throw_dice(num_throws: int, num_dice: int = 2) -> list[float]: """ Return probability list of all possible sums when throwing dice. @@ -35,7 +36,7 @@ def throw_dice(num_throws: int, num_dice: int = 2) -> List[float]: dices = [Dice() for i in range(num_dice)] count_of_sum = [0] * (len(dices) * Dice.NUM_SIDES + 1) for i in range(num_throws): - count_of_sum[sum([dice.roll() for dice in dices])] += 1 + count_of_sum[sum(dice.roll() for dice in dices)] += 1 probability = [round((count * 100) / num_throws, 2) for count in count_of_sum] return probability[num_dice:] # remove probability of sums that never appear diff --git a/maths/prime_factors.py b/maths/prime_factors.py index 34795dd98d1a..e520ae3a6d04 100644 --- a/maths/prime_factors.py +++ b/maths/prime_factors.py @@ -1,10 +1,10 @@ """ python/black : True """ -from typing import List +from __future__ import annotations -def prime_factors(n: int) -> List[int]: +def prime_factors(n: int) -> list[int]: """ Returns prime factors of n as a list. diff --git a/maths/quadratic_equations_complex_numbers.py b/maths/quadratic_equations_complex_numbers.py index 7c47bdef2297..01a411bc560d 100644 --- a/maths/quadratic_equations_complex_numbers.py +++ b/maths/quadratic_equations_complex_numbers.py @@ -1,8 +1,9 @@ +from __future__ import annotations + from cmath import sqrt -from typing import Tuple -def quadratic_roots(a: int, b: int, c: int) -> Tuple[complex, complex]: +def quadratic_roots(a: int, b: int, c: int) -> tuple[complex, complex]: """ Given the numerical coefficients a, b and c, calculates the roots for any quadratic equation of the form ax^2 + bx + c diff --git a/maths/relu.py b/maths/relu.py index 826ada65fa16..458c6bd5c391 100644 --- a/maths/relu.py +++ b/maths/relu.py @@ -9,12 +9,12 @@ Script inspired from its corresponding Wikipedia article https://en.wikipedia.org/wiki/Rectifier_(neural_networks) """ -from typing import List +from __future__ import annotations import numpy as np -def relu(vector: List[float]): +def relu(vector: list[float]): """ Implements the relu function diff --git a/matrix/inverse_of_matrix.py b/matrix/inverse_of_matrix.py index abbeb79ddbdb..9deca6c3c08e 100644 --- a/matrix/inverse_of_matrix.py +++ b/matrix/inverse_of_matrix.py @@ -1,8 +1,9 @@ +from __future__ import annotations + from decimal import Decimal -from typing import List -def inverse_of_matrix(matrix: List[List[float]]) -> List[List[float]]: +def inverse_of_matrix(matrix: list[list[float]]) -> list[list[float]]: """ A matrix multiplied with its inverse gives the identity matrix. This function finds the inverse of a 2x2 matrix. diff --git a/matrix/matrix_operation.py b/matrix/matrix_operation.py index 3838dab6be09..dca01f9c3183 100644 --- a/matrix/matrix_operation.py +++ b/matrix/matrix_operation.py @@ -2,10 +2,10 @@ Functions for 2D matrix operations """ -from typing import List, Tuple +from __future__ import annotations -def add(*matrix_s: List[list]) -> List[list]: +def add(*matrix_s: list[list]) -> list[list]: """ >>> add([[1,2],[3,4]],[[2,3],[4,5]]) [[3, 5], [7, 9]] @@ -20,7 +20,7 @@ def add(*matrix_s: List[list]) -> List[list]: return [[sum(t) for t in zip(*m)] for m in zip(*matrix_s)] -def subtract(matrix_a: List[list], matrix_b: List[list]) -> List[list]: +def subtract(matrix_a: list[list], matrix_b: list[list]) -> list[list]: """ >>> subtract([[1,2],[3,4]],[[2,3],[4,5]]) [[-1, -1], [-1, -1]] @@ -35,7 +35,7 @@ def subtract(matrix_a: List[list], matrix_b: List[list]) -> List[list]: return [[i - j for i, j in zip(*m)] for m in zip(matrix_a, matrix_b)] -def scalar_multiply(matrix: List[list], n: int) -> List[list]: +def scalar_multiply(matrix: list[list], n: int) -> list[list]: """ >>> scalar_multiply([[1,2],[3,4]],5) [[5, 10], [15, 20]] @@ -45,7 +45,7 @@ def scalar_multiply(matrix: List[list], n: int) -> List[list]: return [[x * n for x in row] for row in matrix] -def multiply(matrix_a: List[list], matrix_b: List[list]) -> List[list]: +def multiply(matrix_a: list[list], matrix_b: list[list]) -> list[list]: """ >>> multiply([[1,2],[3,4]],[[5,5],[7,5]]) [[19, 15], [43, 35]] @@ -67,7 +67,7 @@ def multiply(matrix_a: List[list], matrix_b: List[list]) -> List[list]: ] -def identity(n: int) -> List[list]: +def identity(n: int) -> list[list]: """ :param n: dimension for nxn matrix :type n: int @@ -79,7 +79,7 @@ def identity(n: int) -> List[list]: return [[int(row == column) for column in range(n)] for row in range(n)] -def transpose(matrix: List[list], return_map: bool = True) -> List[list]: +def transpose(matrix: list[list], return_map: bool = True) -> list[list]: """ >>> transpose([[1,2],[3,4]]) # doctest: +ELLIPSIS List[list]: return list(map(list, zip(*matrix))) -def minor(matrix: List[list], row: int, column: int) -> List[list]: +def minor(matrix: list[list], row: int, column: int) -> list[list]: """ >>> minor([[1, 2], [3, 4]], 1, 1) [[1]] @@ -102,7 +102,7 @@ def minor(matrix: List[list], row: int, column: int) -> List[list]: return [row[:column] + row[column + 1 :] for row in minor] -def determinant(matrix: List[list]) -> int: +def determinant(matrix: list[list]) -> int: """ >>> determinant([[1, 2], [3, 4]]) -2 @@ -118,7 +118,7 @@ def determinant(matrix: List[list]) -> int: ) -def inverse(matrix: List[list]) -> List[list]: +def inverse(matrix: list[list]) -> list[list]: """ >>> inverse([[1, 2], [3, 4]]) [[-2.0, 1.0], [1.5, -0.5]] @@ -142,17 +142,17 @@ def inverse(matrix: List[list]) -> List[list]: return scalar_multiply(adjugate, 1 / det) -def _check_not_integer(matrix: List[list]) -> bool: +def _check_not_integer(matrix: list[list]) -> bool: if not isinstance(matrix, int) and not isinstance(matrix[0], int): return True raise TypeError("Expected a matrix, got int/list instead") -def _shape(matrix: List[list]) -> list: +def _shape(matrix: list[list]) -> list: return len(matrix), len(matrix[0]) -def _verify_matrix_sizes(matrix_a: List[list], matrix_b: List[list]) -> Tuple[list]: +def _verify_matrix_sizes(matrix_a: list[list], matrix_b: list[list]) -> tuple[list]: shape = _shape(matrix_a) + _shape(matrix_b) if shape[0] != shape[3] or shape[1] != shape[2]: raise ValueError( diff --git a/matrix/searching_in_sorted_matrix.py b/matrix/searching_in_sorted_matrix.py index 470fc01dfb8f..ca6263a32f50 100644 --- a/matrix/searching_in_sorted_matrix.py +++ b/matrix/searching_in_sorted_matrix.py @@ -1,21 +1,23 @@ -from typing import List, Union +from __future__ import annotations + +from typing import Union def search_in_a_sorted_matrix( - mat: List[list], m: int, n: int, key: Union[int, float] + mat: list[list], m: int, n: int, key: Union[int, float] ) -> None: """ - >>> search_in_a_sorted_matrix(\ - [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 5) + >>> search_in_a_sorted_matrix( + ... [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 5) Key 5 found at row- 1 column- 2 - >>> search_in_a_sorted_matrix(\ - [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 21) + >>> search_in_a_sorted_matrix( + ... [[2, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 21) Key 21 not found - >>> search_in_a_sorted_matrix(\ - [[2.1, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 2.1) + >>> search_in_a_sorted_matrix( + ... [[2.1, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 2.1) Key 2.1 found at row- 1 column- 1 - >>> search_in_a_sorted_matrix(\ - [[2.1, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 2.2) + >>> search_in_a_sorted_matrix( + ... [[2.1, 5, 7], [4, 8, 13], [9, 11, 15], [12, 17, 20]], 3, 3, 2.2) Key 2.2 not found """ i, j = m - 1, 0 diff --git a/other/dijkstra_bankers_algorithm.py b/other/dijkstra_bankers_algorithm.py index 405c10b88495..be7bceba125d 100644 --- a/other/dijkstra_bankers_algorithm.py +++ b/other/dijkstra_bankers_algorithm.py @@ -15,8 +15,9 @@ (https://rosettacode.org/wiki/Banker%27s_algorithm) """ +from __future__ import annotations + import time -from typing import Dict, List import numpy as np @@ -40,9 +41,9 @@ class BankersAlgorithm: def __init__( self, - claim_vector: List[int], - allocated_resources_table: List[List[int]], - maximum_claim_table: List[List[int]], + claim_vector: list[int], + allocated_resources_table: list[list[int]], + maximum_claim_table: list[list[int]], ) -> None: """ :param claim_vector: A nxn/nxm list depicting the amount of each resources @@ -56,7 +57,7 @@ def __init__( self.__allocated_resources_table = allocated_resources_table self.__maximum_claim_table = maximum_claim_table - def __processes_resource_summation(self) -> List[int]: + def __processes_resource_summation(self) -> list[int]: """ Check for allocated resources in line with each resource in the claim vector """ @@ -65,7 +66,7 @@ def __processes_resource_summation(self) -> List[int]: for i in range(len(self.__allocated_resources_table[0])) ] - def __available_resources(self) -> List[int]: + def __available_resources(self) -> list[int]: """ Check for available resources in line with each resource in the claim vector """ @@ -73,7 +74,7 @@ def __available_resources(self) -> List[int]: self.__processes_resource_summation() ) - def __need(self) -> List[List[int]]: + def __need(self) -> list[list[int]]: """ Implement safety checker that calculates the needs by ensuring that max_claim[i][j] - alloc_table[i][j] <= avail[j] @@ -83,7 +84,7 @@ def __need(self) -> List[List[int]]: for i, allocated_resource in enumerate(self.__allocated_resources_table) ] - def __need_index_manager(self) -> Dict[int, List[int]]: + def __need_index_manager(self) -> dict[int, list[int]]: """ This function builds an index control dictionary to track original ids/indices of processes when altered during execution of method "main" diff --git a/other/markov_chain.py b/other/markov_chain.py index 9b13fa515709..b93c408cd288 100644 --- a/other/markov_chain.py +++ b/other/markov_chain.py @@ -1,6 +1,7 @@ +from __future__ import annotations + from collections import Counter from random import random -from typing import Dict, List, Tuple class MarkovChainGraphUndirectedUnweighted: @@ -23,7 +24,7 @@ def add_transition_probability( self.add_node(node2) self.connections[node1][node2] = probability - def get_nodes(self) -> List[str]: + def get_nodes(self) -> list[str]: return list(self.connections) def transition(self, node: str) -> str: @@ -37,8 +38,8 @@ def transition(self, node: str) -> str: def get_transitions( - start: str, transitions: List[Tuple[str, str, float]], steps: int -) -> Dict[str, int]: + start: str, transitions: list[tuple[str, str, float]], steps: int +) -> dict[str, int]: """ Running Markov Chain algorithm and calculating the number of times each node is visited diff --git a/other/triplet_sum.py b/other/triplet_sum.py index 25fed5d54579..0e78bb52bb72 100644 --- a/other/triplet_sum.py +++ b/other/triplet_sum.py @@ -3,13 +3,14 @@ we are required to find a triplet from the array such that it's sum is equal to the target. """ +from __future__ import annotations + from itertools import permutations from random import randint from timeit import repeat -from typing import List, Tuple -def make_dataset() -> Tuple[List[int], int]: +def make_dataset() -> tuple[list[int], int]: arr = [randint(-1000, 1000) for i in range(10)] r = randint(-5000, 5000) return (arr, r) @@ -18,7 +19,7 @@ def make_dataset() -> Tuple[List[int], int]: dataset = make_dataset() -def triplet_sum1(arr: List[int], target: int) -> Tuple[int, int, int]: +def triplet_sum1(arr: list[int], target: int) -> tuple[int, int, int]: """ Returns a triplet in the array with sum equal to target, else (0, 0, 0). @@ -37,7 +38,7 @@ def triplet_sum1(arr: List[int], target: int) -> Tuple[int, int, int]: return (0, 0, 0) -def triplet_sum2(arr: List[int], target: int) -> Tuple[int, int, int]: +def triplet_sum2(arr: list[int], target: int) -> tuple[int, int, int]: """ Returns a triplet in the array with sum equal to target, else (0, 0, 0). @@ -64,7 +65,7 @@ def triplet_sum2(arr: List[int], target: int) -> Tuple[int, int, int]: return (0, 0, 0) -def solution_times() -> Tuple[float, float]: +def solution_times() -> tuple[float, float]: setup_code = """ from __main__ import dataset, triplet_sum1, triplet_sum2 """ diff --git a/other/two_sum.py b/other/two_sum.py index 8ac7a18ba9a4..5209acbc7e44 100644 --- a/other/two_sum.py +++ b/other/two_sum.py @@ -11,21 +11,37 @@ Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1]. """ +from __future__ import annotations -def twoSum(nums, target): +def two_sum(nums: list[int], target: int) -> list[int]: """ - :type nums: List[int] - :type target: int - :rtype: List[int] + >>> two_sum([2, 7, 11, 15], 9) + [0, 1] + >>> two_sum([15, 2, 11, 7], 13) + [1, 2] + >>> two_sum([2, 7, 11, 15], 17) + [0, 3] + >>> two_sum([7, 15, 11, 2], 18) + [0, 2] + >>> two_sum([2, 7, 11, 15], 26) + [2, 3] + >>> two_sum([2, 7, 11, 15], 8) + [] + >>> two_sum([3 * i for i in range(10)], 19) + [] """ chk_map = {} for index, val in enumerate(nums): compl = target - val if compl in chk_map: - indices = [chk_map[compl], index] - print(indices) - return [indices] - else: - chk_map[val] = index - return False + return [chk_map[compl], index] + chk_map[val] = index + return [] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{two_sum([2, 7, 11, 15], 9) = }") diff --git a/project_euler/problem_09/sol1.py b/project_euler/problem_09/sol1.py index 3bb5c968115d..caba6b1b1530 100644 --- a/project_euler/problem_09/sol1.py +++ b/project_euler/problem_09/sol1.py @@ -16,7 +16,6 @@ def solution(): 1. a < b < c 2. a**2 + b**2 = c**2 3. a + b + c = 1000 - # The code below has been commented due to slow execution affecting Travis. # >>> solution() # 31875000 @@ -30,6 +29,40 @@ def solution(): return a * b * c +def solution_fast(): + """ + Returns the product of a,b,c which are Pythagorean Triplet that satisfies + the following: + 1. a < b < c + 2. a**2 + b**2 = c**2 + 3. a + b + c = 1000 + + # The code below has been commented due to slow execution affecting Travis. + # >>> solution_fast() + # 31875000 + """ + for a in range(300): + for b in range(400): + c = 1000 - a - b + if a < b < c and (a ** 2) + (b ** 2) == (c ** 2): + return a * b * c + + +def benchmark() -> None: + """ + Benchmark code comparing two different version function. + """ + import timeit + + print( + timeit.timeit("solution()", setup="from __main__ import solution", number=1000) + ) + print( + timeit.timeit( + "solution_fast()", setup="from __main__ import solution_fast", number=1000 + ) + ) + + if __name__ == "__main__": - print("Please Wait...") - print(solution()) + benchmark() diff --git a/project_euler/problem_09/sol3.py b/project_euler/problem_09/sol3.py index a6df46a3a66b..ed27f089bd40 100644 --- a/project_euler/problem_09/sol3.py +++ b/project_euler/problem_09/sol3.py @@ -25,11 +25,10 @@ def solution(): # 31875000 """ return [ - a * b * c + a * b * (1000 - a - b) for a in range(1, 999) for b in range(a, 999) - for c in range(b, 999) - if (a * a + b * b == c * c) and (a + b + c == 1000) + if (a * a + b * b == (1000 - a - b) ** 2) ][0] diff --git a/project_euler/problem_35/sol1.py b/project_euler/problem_35/sol1.py index c47eb7d82f54..5f023c56ae50 100644 --- a/project_euler/problem_35/sol1.py +++ b/project_euler/problem_35/sol1.py @@ -10,7 +10,7 @@ we will rule out the numbers which contain an even digit. After this we will generate each circular combination of the number and check if all are prime. """ -from typing import List +from __future__ import annotations seive = [True] * 1000001 i = 2 @@ -47,7 +47,7 @@ def contains_an_even_digit(n: int) -> bool: return any(digit in "02468" for digit in str(n)) -def find_circular_primes(limit: int = 1000000) -> List[int]: +def find_circular_primes(limit: int = 1000000) -> list[int]: """ Return circular primes below limit. >>> len(find_circular_primes(100)) diff --git a/project_euler/problem_37/sol1.py b/project_euler/problem_37/sol1.py index c01d64d83fbe..e3aec5a844fe 100644 --- a/project_euler/problem_37/sol1.py +++ b/project_euler/problem_37/sol1.py @@ -9,8 +9,7 @@ NOTE: 2, 3, 5, and 7 are not considered to be truncatable primes. """ - -from typing import List +from __future__ import annotations seive = [True] * 1000001 seive[1] = False @@ -36,7 +35,7 @@ def is_prime(n: int) -> bool: return seive[n] -def list_truncated_nums(n: int) -> List[int]: +def list_truncated_nums(n: int) -> list[int]: """ Returns a list of all left and right truncated numbers of n >>> list_truncated_nums(927628) @@ -71,7 +70,7 @@ def validate(n: int) -> bool: return True -def compute_truncated_primes(count: int = 11) -> List[int]: +def compute_truncated_primes(count: int = 11) -> list[int]: """ Returns the list of truncated primes >>> compute_truncated_primes(11) diff --git a/project_euler/problem_39/sol1.py b/project_euler/problem_39/sol1.py index b0a5d5188fed..79fa309f01c5 100644 --- a/project_euler/problem_39/sol1.py +++ b/project_euler/problem_39/sol1.py @@ -6,11 +6,12 @@ For which value of p ≤ 1000, is the number of solutions maximised? """ +from __future__ import annotations + from collections import Counter -from typing import Dict -def pythagorean_triple(max_perimeter: int) -> Dict: +def pythagorean_triple(max_perimeter: int) -> dict: """ Returns a dictionary with keys as the perimeter of a right angled triangle and value as the number of corresponding triplets. diff --git a/project_euler/problem_41/sol1.py b/project_euler/problem_41/sol1.py index 4ed09ccb8565..b4c0d842ae25 100644 --- a/project_euler/problem_41/sol1.py +++ b/project_euler/problem_41/sol1.py @@ -1,6 +1,7 @@ +from __future__ import annotations + from itertools import permutations from math import sqrt -from typing import List """ We shall say that an n-digit number is pandigital if it makes use of all the digits @@ -34,7 +35,7 @@ def is_prime(n: int) -> bool: return True -def compute_pandigital_primes(n: int) -> List[int]: +def compute_pandigital_primes(n: int) -> list[int]: """ Returns a list of all n-digit pandigital primes. >>> compute_pandigital_primes(2) diff --git a/project_euler/problem_46/sol1.py b/project_euler/problem_46/sol1.py index 761e9b8cc7fb..e94e9247d86b 100644 --- a/project_euler/problem_46/sol1.py +++ b/project_euler/problem_46/sol1.py @@ -15,7 +15,7 @@ prime and twice a square? """ -from typing import List +from __future__ import annotations seive = [True] * 100001 i = 2 @@ -43,7 +43,7 @@ def is_prime(n: int) -> bool: odd_composites = [num for num in range(3, len(seive), 2) if not is_prime(num)] -def compute_nums(n: int) -> List[int]: +def compute_nums(n: int) -> list[int]: """ Returns a list of first n odd composite numbers which do not follow the conjecture. diff --git a/project_euler/problem_48/sol1.py b/project_euler/problem_48/sol1.py index 06ad1408dcef..01ff702d9cd5 100644 --- a/project_euler/problem_48/sol1.py +++ b/project_euler/problem_48/sol1.py @@ -2,14 +2,15 @@ Self Powers Problem 48 -The series, 11 + 22 + 33 + ... + 1010 = 10405071317. +The series, 1^1 + 2^2 + 3^3 + ... + 10^10 = 10405071317. -Find the last ten digits of the series, 11 + 22 + 33 + ... + 10001000. +Find the last ten digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. """ def solution(): - """Returns the last 10 digits of the series, 11 + 22 + 33 + ... + 10001000. + """ + Returns the last 10 digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. >>> solution() '9110846700' diff --git a/project_euler/problem_54/__init__.py b/project_euler/problem_54/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_54/poker_hands.txt b/project_euler/problem_54/poker_hands.txt new file mode 100644 index 000000000000..9ab00248c061 --- /dev/null +++ b/project_euler/problem_54/poker_hands.txt @@ -0,0 +1,1000 @@ +8C TS KC 9H 4S 7D 2S 5D 3S AC +5C AD 5D AC 9C 7C 5H 8D TD KS +3H 7H 6S KC JS QH TD JC 2D 8S +TH 8H 5C QS TC 9H 4D JC KS JS +7C 5H KC QH JD AS KH 4C AD 4S +5H KS 9C 7D 9H 8D 3S 5D 5C AH +6H 4H 5C 3H 2H 3S QH 5S 6S AS +TD 8C 4H 7C TC KC 4C 3H 7S KS +7C 9C 6D KD 3H 4C QS QC AC KH +JC 6S 5H 2H 2D KD 9D 7C AS JS +AD QH TH 9D 8H TS 6D 3S AS AC +2H 4S 5C 5S TC KC JD 6C TS 3C +QD AS 6H JS 2C 3D 9H KC 4H 8S +KD 8S 9S 7C 2S 3S 6D 6S 4H KC +3C 8C 2D 7D 4D 9S 4S QH 4H JD +8C KC 7S TC 2D TS 8H QD AC 5C +3D KH QD 6C 6S AD AS 8H 2H QS +6S 8D 4C 8S 6C QH TC 6D 7D 9D +2S 8D 8C 4C TS 9S 9D 9C AC 3D +3C QS 2S 4H JH 3D 2D TD 8S 9H +5H QS 8S 6D 3C 8C JD AS 7H 7D +6H TD 9D AS JH 6C QC 9S KD JC +AH 8S QS 4D TH AC TS 3C 3D 5C +5S 4D JS 3D 8H 6C TS 3S AD 8C +6D 7C 5D 5H 3S 5C JC 2H 5S 3D +5H 6H 2S KS 3D 5D JD 7H JS 8H +KH 4H AS JS QS QC TC 6D 7C KS +3D QS TS 2H JS 4D AS 9S JC KD +QD 5H 4D 5D KH 7H 3D JS KD 4H +2C 9H 6H 5C 9D 6C JC 2D TH 9S +7D 6D AS QD JH 4D JS 7C QS 5C +3H KH QD AD 8C 8H 3S TH 9D 5S +AH 9S 4D 9D 8S 4H JS 3C TC 8D +2C KS 5H QD 3S TS 9H AH AD 8S +5C 7H 5D KD 9H 4D 3D 2D KS AD +KS KC 9S 6D 2C QH 9D 9H TS TC +9C 6H 5D QH 4D AD 6D QC JS KH +9S 3H 9D JD 5C 4D 9H AS TC QH +2C 6D JC 9C 3C AD 9S KH 9D 7D +KC 9C 7C JC JS KD 3H AS 3C 7D +QD KH QS 2C 3S 8S 8H 9H 9C JC +QH 8D 3C KC 4C 4H 6D AD 9H 9D +3S KS QS 7H KH 7D 5H 5D JD AD +2H 2C 6H TH TC 7D 8D 4H 8C AS +4S 2H AC QC 3S 6D TH 4D 4C KH +4D TC KS AS 7C 3C 6D 2D 9H 6C +8C TD 5D QS 2C 7H 4C 9C 3H 9H +5H JH TS 7S TD 6H AD QD 8H 8S +5S AD 9C 8C 7C 8D 5H 9D 8S 2S +4H KH KS 9S 2S KC 5S AD 4S 7D +QS 9C QD 6H JS 5D AC 8D 2S AS +KH AC JC 3S 9D 9S 3C 9C 5S JS +AD 3C 3D KS 3S 5C 9C 8C TS 4S +JH 8D 5D 6H KD QS QD 3D 6C KC +8S JD 6C 3S 8C TC QC 3C QH JS +KC JC 8H 2S 9H 9C JH 8S 8C 9S +8S 2H QH 4D QC 9D KC AS TH 3C +8S 6H TH 7C 2H 6S 3C 3H AS 7S +QH 5S JS 4H 5H TS 8H AH AC JC +9D 8H 2S 4S TC JC 3C 7H 3H 5C +3D AD 3C 3S 4C QC AS 5D TH 8C +6S 9D 4C JS KH AH TS JD 8H AD +4C 6S 9D 7S AC 4D 3D 3S TC JD +AD 7H 6H 4H JH KC TD TS 7D 6S +8H JH TC 3S 8D 8C 9S 2C 5C 4D +2C 9D KC QH TH QS JC 9C 4H TS +QS 3C QD 8H KH 4H 8D TD 8S AC +7C 3C TH 5S 8H 8C 9C JD TC KD +QC TC JD TS 8C 3H 6H KD 7C TD +JH QS KS 9C 6D 6S AS 9H KH 6H +2H 4D AH 2D JH 6H TD 5D 4H JD +KD 8C 9S JH QD JS 2C QS 5C 7C +4S TC 7H 8D 2S 6H 7S 9C 7C KC +8C 5D 7H 4S TD QC 8S JS 4H KS +AD 8S JH 6D TD KD 7C 6C 2D 7D +JC 6H 6S JS 4H QH 9H AH 4C 3C +6H 5H AS 7C 7S 3D KH KC 5D 5C +JC 3D TD AS 4D 6D 6S QH JD KS +8C 7S 8S QH 2S JD 5C 7H AH QD +8S 3C 6H 6C 2C 8D TD 7D 4C 4D +5D QH KH 7C 2S 7H JS 6D QC QD +AD 6C 6S 7D TH 6H 2H 8H KH 4H +KS JS KD 5D 2D KH 7D 9C 8C 3D +9C 6D QD 3C KS 3S 7S AH JD 2D +AH QH AS JC 8S 8H 4C KC TH 7D +JC 5H TD 7C 5D KD 4C AD 8H JS +KC 2H AC AH 7D JH KH 5D 7S 6D +9S 5S 9C 6H 8S TD JD 9H 6C AC +7D 8S 6D TS KD 7H AC 5S 7C 5D +AH QC JC 4C TC 8C 2H TS 2C 7D +KD KC 6S 3D 7D 2S 8S 3H 5S 5C +8S 5D 8H 4C 6H KC 3H 7C 5S KD +JH 8C 3D 3C 6C KC TD 7H 7C 4C +JC KC 6H TS QS TD KS 8H 8C 9S +6C 5S 9C QH 7D AH KS KC 9S 2C +4D 4S 8H TD 9C 3S 7D 9D AS TH +6S 7D 3C 6H 5D KD 2C 5C 9D 9C +2H KC 3D AD 3H QD QS 8D JC 4S +8C 3H 9C 7C AD 5D JC 9D JS AS +5D 9H 5C 7H 6S 6C QC JC QD 9S +JC QS JH 2C 6S 9C QC 3D 4S TC +4H 5S 8D 3D 4D 2S KC 2H JS 2C +TD 3S TH KD 4D 7H JH JS KS AC +7S 8C 9S 2D 8S 7D 5C AD 9D AS +8C 7H 2S 6C TH 3H 4C 3S 8H AC +KD 5H JC 8H JD 2D 4H TD JH 5C +3D AS QH KS 7H JD 8S 5S 6D 5H +9S 6S TC QS JC 5C 5D 9C TH 8C +5H 3S JH 9H 2S 2C 6S 7S AS KS +8C QD JC QS TC QC 4H AC KH 6C +TC 5H 7D JH 4H 2H 8D JC KS 4D +5S 9C KH KD 9H 5C TS 3D 7D 2D +5H AS TC 4D 8C 2C TS 9D 3H 8D +6H 8D 2D 9H JD 6C 4S 5H 5S 6D +AD 9C JC 7D 6H 9S 6D JS 9H 3C +AD JH TC QS 4C 5D 9S 7C 9C AH +KD 6H 2H TH 8S QD KS 9D 9H AS +4H 8H 8D 5H 6C AH 5S AS AD 8S +QS 5D 4S 2H TD KS 5H AC 3H JC +9C 7D QD KD AC 6D 5H QH 6H 5S +KC AH QH 2H 7D QS 3H KS 7S JD +6C 8S 3H 6D KS QD 5D 5C 8H TC +9H 4D 4S 6S 9D KH QC 4H 6C JD +TD 2D QH 4S 6H JH KD 3C QD 8C +4S 6H 7C QD 9D AS AH 6S AD 3C +2C KC TH 6H 8D AH 5C 6D 8S 5D +TD TS 7C AD JC QD 9H 3C KC 7H +5D 4D 5S 8H 4H 7D 3H JD KD 2D +JH TD 6H QS 4S KD 5C 8S 7D 8H +AC 3D AS 8C TD 7H KH 5D 6C JD +9D KS 7C 6D QH TC JD KD AS KC +JH 8S 5S 7S 7D AS 2D 3D AD 2H +2H 5D AS 3C QD KC 6H 9H 9S 2C +9D 5D TH 4C JH 3H 8D TC 8H 9H +6H KD 2C TD 2H 6C 9D 2D JS 8C +KD 7S 3C 7C AS QH TS AD 8C 2S +QS 8H 6C JS 4C 9S QC AD TD TS +2H 7C TS TC 8C 3C 9H 2D 6D JC +TC 2H 8D JH KS 6D 3H TD TH 8H +9D TD 9H QC 5D 6C 8H 8C KC TS +2H 8C 3D AH 4D TH TC 7D 8H KC +TS 5C 2D 8C 6S KH AH 5H 6H KC +5S 5D AH TC 4C JD 8D 6H 8C 6C +KC QD 3D 8H 2D JC 9H 4H AD 2S +TD 6S 7D JS KD 4H QS 2S 3S 8C +4C 9H JH TS 3S 4H QC 5S 9S 9C +2C KD 9H JS 9S 3H JC TS 5D AC +AS 2H 5D AD 5H JC 7S TD JS 4C +2D 4S 8H 3D 7D 2C AD KD 9C TS +7H QD JH 5H JS AC 3D TH 4C 8H +6D KH KC QD 5C AD 7C 2D 4H AC +3D 9D TC 8S QD 2C JC 4H JD AH +6C TD 5S TC 8S AH 2C 5D AS AC +TH 7S 3D AS 6C 4C 7H 7D 4H AH +5C 2H KS 6H 7S 4H 5H 3D 3C 7H +3C 9S AC 7S QH 2H 3D 6S 3S 3H +2D 3H AS 2C 6H TC JS 6S 9C 6C +QH KD QD 6D AC 6H KH 2C TS 8C +8H 7D 3S 9H 5D 3H 4S QC 9S 5H +2D 9D 7H 6H 3C 8S 5H 4D 3S 4S +KD 9S 4S TC 7S QC 3S 8S 2H 7H +TC 3D 8C 3H 6C 2H 6H KS KD 4D +KC 3D 9S 3H JS 4S 8H 2D 6C 8S +6H QS 6C TC QD 9H 7D 7C 5H 4D +TD 9D 8D 6S 6C TC 5D TS JS 8H +4H KC JD 9H TC 2C 6S 5H 8H AS +JS 9C 5C 6S 9D JD 8H KC 4C 6D +4D 8D 8S 6C 7C 6H 7H 8H 5C KC +TC 3D JC 6D KS 9S 6H 7S 9C 2C +6C 3S KD 5H TS 7D 9H 9S 6H KH +3D QD 4C 6H TS AC 3S 5C 2H KD +4C AS JS 9S 7C TS 7H 9H JC KS +4H 8C JD 3H 6H AD 9S 4S 5S KS +4C 2C 7D 3D AS 9C 2S QS KC 6C +8S 5H 3D 2S AC 9D 6S 3S 4D TD +QD TH 7S TS 3D AC 7H 6C 5D QC +TC QD AD 9C QS 5C 8D KD 3D 3C +9D 8H AS 3S 7C 8S JD 2D 8D KC +4C TH AC QH JS 8D 7D 7S 9C KH +9D 8D 4C JH 2C 2S QD KD TS 4H +4D 6D 5D 2D JH 3S 8S 3H TC KH +AD 4D 2C QS 8C KD JH JD AH 5C +5C 6C 5H 2H JH 4H KS 7C TC 3H +3C 4C QC 5D JH 9C QD KH 8D TC +3H 9C JS 7H QH AS 7C 9H 5H JC +2D 5S QD 4S 3C KC 6S 6C 5C 4C +5D KH 2D TS 8S 9C AS 9S 7C 4C +7C AH 8C 8D 5S KD QH QS JH 2C +8C 9D AH 2H AC QC 5S 8H 7H 2C +QD 9H 5S QS QC 9C 5H JC TH 4H +6C 6S 3H 5H 3S 6H KS 8D AC 7S +AC QH 7H 8C 4S KC 6C 3D 3S TC +9D 3D JS TH AC 5H 3H 8S 3S TC +QD KH JS KS 9S QC 8D AH 3C AC +5H 6C KH 3S 9S JH 2D QD AS 8C +6C 4D 7S 7H 5S JC 6S 9H 4H JH +AH 5S 6H 9S AD 3S TH 2H 9D 8C +4C 8D 9H 7C QC AD 4S 9C KC 5S +9D 6H 4D TC 4C JH 2S 5D 3S AS +2H 6C 7C KH 5C AD QS TH JD 8S +3S 4S 7S AH AS KC JS 2S AD TH +JS KC 2S 7D 8C 5C 9C TS 5H 9D +7S 9S 4D TD JH JS KH 6H 5D 2C +JD JS JC TH 2D 3D QD 8C AC 5H +7S KH 5S 9D 5D TD 4S 6H 3C 2D +4S 5D AC 8D 4D 7C AD AS AH 9C +6S TH TS KS 2C QC AH AS 3C 4S +2H 8C 3S JC 5C 7C 3H 3C KH JH +7S 3H JC 5S 6H 4C 2S 4D KC 7H +4D 7C 4H 9S 8S 6S AD TC 6C JC +KH QS 3S TC 4C 8H 8S AC 3C TS +QD QS TH 3C TS 7H 7D AH TD JC +TD JD QC 4D 9S 7S TS AD 7D AC +AH 7H 4S 6D 7C 2H 9D KS JC TD +7C AH JD 4H 6D QS TS 2H 2C 5C +TC KC 8C 9S 4C JS 3C JC 6S AH +AS 7D QC 3D 5S JC JD 9D TD KH +TH 3C 2S 6H AH AC 5H 5C 7S 8H +QC 2D AC QD 2S 3S JD QS 6S 8H +KC 4H 3C 9D JS 6H 3S 8S AS 8C +7H KC 7D JD 2H JC QH 5S 3H QS +9H TD 3S 8H 7S AC 5C 6C AH 7C +8D 9H AH JD TD QS 7D 3S 9C 8S +AH QH 3C JD KC 4S 5S 5D TD KS +9H 7H 6S JH TH 4C 7C AD 5C 2D +7C KD 5S TC 9D 6S 6C 5D 2S TH +KC 9H 8D 5H 7H 4H QC 3D 7C AS +6S 8S QC TD 4S 5C TH QS QD 2S +8S 5H TH QC 9H 6S KC 7D 7C 5C +7H KD AH 4D KH 5C 4S 2D KC QH +6S 2C TD JC AS 4D 6C 8C 4H 5S +JC TC JD 5S 6S 8D AS 9D AD 3S +6D 6H 5D 5S TC 3D 7D QS 9D QD +4S 6C 8S 3S 7S AD KS 2D 7D 7C +KC QH JC AC QD 5D 8D QS 7H 7D +JS AH 8S 5H 3D TD 3H 4S 6C JH +4S QS 7D AS 9H JS KS 6D TC 5C +2D 5C 6H TC 4D QH 3D 9H 8S 6C +6D 7H TC TH 5S JD 5C 9C KS KD +8D TD QH 6S 4S 6C 8S KC 5C TC +5S 3D KS AC 4S 7D QD 4C TH 2S +TS 8H 9S 6S 7S QH 3C AH 7H 8C +4C 8C TS JS QC 3D 7D 5D 7S JH +8S 7S 9D QC AC 7C 6D 2H JH KC +JS KD 3C 6S 4S 7C AH QC KS 5H +KS 6S 4H JD QS TC 8H KC 6H AS +KH 7C TC 6S TD JC 5C 7D AH 3S +3H 4C 4H TC TH 6S 7H 6D 9C QH +7D 5H 4S 8C JS 4D 3D 8S QH KC +3H 6S AD 7H 3S QC 8S 4S 7S JS +3S JD KH TH 6H QS 9C 6C 2D QD +4S QH 4D 5H KC 7D 6D 8D TH 5S +TD AD 6S 7H KD KH 9H 5S KC JC +3H QC AS TS 4S QD KS 9C 7S KC +TS 6S QC 6C TH TC 9D 5C 5D KD +JS 3S 4H KD 4C QD 6D 9S JC 9D +8S JS 6D 4H JH 6H 6S 6C KS KH +AC 7D 5D TC 9S KH 6S QD 6H AS +AS 7H 6D QH 8D TH 2S KH 5C 5H +4C 7C 3D QC TC 4S KH 8C 2D JS +6H 5D 7S 5H 9C 9H JH 8S TH 7H +AS JS 2S QD KH 8H 4S AC 8D 8S +3H 4C TD KD 8C JC 5C QS 2D JD +TS 7D 5D 6C 2C QS 2H 3C AH KS +4S 7C 9C 7D JH 6C 5C 8H 9D QD +2S TD 7S 6D 9C 9S QS KH QH 5C +JC 6S 9C QH JH 8D 7S JS KH 2H +8D 5H TH KC 4D 4S 3S 6S 3D QS +2D JD 4C TD 7C 6D TH 7S JC AH +QS 7S 4C TH 9D TS AD 4D 3H 6H +2D 3H 7D JD 3D AS 2S 9C QC 8S +4H 9H 9C 2C 7S JH KD 5C 5D 6H +TC 9H 8H JC 3C 9S 8D KS AD KC +TS 5H JD QS QH QC 8D 5D KH AH +5D AS 8S 6S 4C AH QC QD TH 7H +3H 4H 7D 6S 4S 9H AS 8H JS 9D +JD 8C 2C 9D 7D 5H 5S 9S JC KD +KD 9C 4S QD AH 7C AD 9D AC TD +6S 4H 4S 9C 8D KS TC 9D JH 7C +5S JC 5H 4S QH AC 2C JS 2S 9S +8C 5H AS QD AD 5C 7D 8S QC TD +JC 4C 8D 5C KH QS 4D 6H 2H 2C +TH 4S 2D KC 3H QD AC 7H AD 9D +KH QD AS 8H TH KC 8D 7S QH 8C +JC 6C 7D 8C KH AD QS 2H 6S 2D +JC KH 2D 7D JS QC 5H 4C 5D AD +TS 3S AD 4S TD 2D TH 6S 9H JH +9H 2D QS 2C 4S 3D KH AS AC 9D +KH 6S 8H 4S KD 7D 9D TS QD QC +JH 5H AH KS AS AD JC QC 5S KH +5D 7D 6D KS KD 3D 7C 4D JD 3S +AC JS 8D 5H 9C 3H 4H 4D TS 2C +6H KS KH 9D 7C 2S 6S 8S 2H 3D +6H AC JS 7S 3S TD 8H 3H 4H TH +9H TC QC KC 5C KS 6H 4H AC 8S +TC 7D QH 4S JC TS 6D 6C AC KH +QH 7D 7C JH QS QD TH 3H 5D KS +3D 5S 8D JS 4C 2C KS 7H 9C 4H +5H 8S 4H TD 2C 3S QD QC 3H KC +QC JS KD 9C AD 5S 9D 7D 7H TS +8C JC KH 7C 7S 6C TS 2C QD TH +5S 9D TH 3C 7S QH 8S 9C 2H 5H +5D 9H 6H 2S JS KH 3H 7C 2H 5S +JD 5D 5S 2C TC 2S 6S 6C 3C 8S +4D KH 8H 4H 2D KS 3H 5C 2S 9H +3S 2D TD 7H 8S 6H JD KC 9C 8D +6S QD JH 7C 9H 5H 8S 8H TH TD +QS 7S TD 7D TS JC KD 7C 3C 2C +3C JD 8S 4H 2D 2S TD AS 4D AC +AH KS 6C 4C 4S 7D 8C 9H 6H AS +5S 3C 9S 2C QS KD 4D 4S AC 5D +2D TS 2C JS KH QH 5D 8C AS KC +KD 3H 6C TH 8S 7S KH 6H 9S AC +6H 7S 6C QS AH 2S 2H 4H 5D 5H +5H JC QD 2C 2S JD AS QC 6S 7D +6C TC AS KD 8H 9D 2C 7D JH 9S +2H 4C 6C AH 8S TD 3H TH 7C TS +KD 4S TS 6C QH 8D 9D 9C AH 7D +6D JS 5C QD QC 9C 5D 8C 2H KD +3C QH JH AD 6S AH KC 8S 6D 6H +3D 7C 4C 7S 5S 3S 6S 5H JC 3C +QH 7C 5H 3C 3S 8C TS 4C KD 9C +QD 3S 7S 5H 7H QH JC 7C 8C KD +3C KD KH 2S 4C TS AC 6S 2C 7C +2C KH 3C 4C 6H 4D 5H 5S 7S QD +4D 7C 8S QD TS 9D KS 6H KD 3C +QS 4D TS 7S 4C 3H QD 8D 9S TC +TS QH AC 6S 3C 9H 9D QS 8S 6H +3S 7S 5D 4S JS 2D 6C QH 6S TH +4C 4H AS JS 5D 3D TS 9C AC 8S +6S 9C 7C 3S 5C QS AD AS 6H 3C +9S 8C 7H 3H 6S 7C AS 9H JD KH +3D 3H 7S 4D 6C 7C AC 2H 9C TH +4H 5S 3H AC TC TH 9C 9H 9S 8D +8D 9H 5H 4D 6C 2H QD 6S 5D 3S +4C 5C JD QS 4D 3H TH AC QH 8C +QC 5S 3C 7H AD 4C KS 4H JD 6D +QS AH 3H KS 9H 2S JS JH 5H 2H +2H 5S TH 6S TS 3S KS 3C 5H JS +2D 9S 7H 3D KC JH 6D 7D JS TD +AC JS 8H 2C 8C JH JC 2D TH 7S +5D 9S 8H 2H 3D TC AH JC KD 9C +9D QD JC 2H 6D KH TS 9S QH TH +2C 8D 4S JD 5H 3H TH TC 9C KC +AS 3D 9H 7D 4D TH KH 2H 7S 3H +4H 7S KS 2S JS TS 8S 2H QD 8D +5S 6H JH KS 8H 2S QC AC 6S 3S +JC AS AD QS 8H 6C KH 4C 4D QD +2S 3D TS TD 9S KS 6S QS 5C 8D +3C 6D 4S QC KC JH QD TH KH AD +9H AH 4D KS 2S 8D JH JC 7C QS +2D 6C TH 3C 8H QD QH 2S 3S KS +6H 5D 9S 4C TS TD JS QD 9D JD +5H 8H KH 8S KS 7C TD AD 4S KD +2C 7C JC 5S AS 6C 7D 8S 5H 9C +6S QD 9S TS KH QS 5S QH 3C KC +7D 3H 3C KD 5C AS JH 7H 6H JD +9D 5C 9H KC 8H KS 4S AD 4D 2S +3S JD QD 8D 2S 7C 5S 6S 5H TS +6D 9S KC TD 3S 6H QD JD 5C 8D +5H 9D TS KD 8D 6H TD QC 4C 7D +6D 4S JD 9D AH 9S AS TD 9H QD +2D 5S 2H 9C 6H 9S TD QC 7D TC +3S 2H KS TS 2C 9C 8S JS 9D 7D +3C KC 6D 5D 6C 6H 8S AS 7S QS +JH 9S 2H 8D 4C 8H 9H AD TH KH +QC AS 2S JS 5C 6H KD 3H 7H 2C +QD 8H 2S 8D 3S 6D AH 2C TC 5C +JD JS TS 8S 3H 5D TD KC JC 6H +6S QS TC 3H 5D AH JC 7C 7D 4H +7C 5D 8H 9C 2H 9H JH KH 5S 2C +9C 7H 6S TH 3S QC QD 4C AC JD +2H 5D 9S 7D KC 3S QS 2D AS KH +2S 4S 2H 7D 5C TD TH QH 9S 4D +6D 3S TS 6H 4H KS 9D 8H 5S 2D +9H KS 4H 3S 5C 5D KH 6H 6S JS +KC AS 8C 4C JC KH QC TH QD AH +6S KH 9S 2C 5H TC 3C 7H JC 4D +JD 4S 6S 5S 8D 7H 7S 4D 4C 2H +7H 9H 5D KH 9C 7C TS TC 7S 5H +4C 8D QC TS 4S 9H 3D AD JS 7C +8C QS 5C 5D 3H JS AH KC 4S 9D +TS JD 8S QS TH JH KH 2D QD JS +JD QC 5D 6S 9H 3S 2C 8H 9S TS +2S 4C AD 7H JC 5C 2D 6D 4H 3D +7S JS 2C 4H 8C AD QD 9C 3S TD +JD TS 4C 6H 9H 7D QD 6D 3C AS +AS 7C 4C 6S 5D 5S 5C JS QC 4S +KD 6S 9S 7C 3C 5S 7D JH QD JS +4S 7S JH 2C 8S 5D 7H 3D QH AD +TD 6H 2H 8D 4H 2D 7C AD KH 5D +TS 3S 5H 2C QD AH 2S 5C KH TD +KC 4D 8C 5D AS 6C 2H 2S 9H 7C +KD JS QC TS QS KH JH 2C 5D AD +3S 5H KC 6C 9H 3H 2H AD 7D 7S +7S JS JH KD 8S 7D 2S 9H 7C 2H +9H 2D 8D QC 6S AD AS 8H 5H 6C +2S 7H 6C 6D 7D 8C 5D 9D JC 3C +7C 9C 7H JD 2H KD 3S KH AD 4S +QH AS 9H 4D JD KS KD TS KH 5H +4C 8H 5S 3S 3D 7D TD AD 7S KC +JS 8S 5S JC 8H TH 9C 4D 5D KC +7C 5S 9C QD 2C QH JS 5H 8D KH +TD 2S KS 3D AD KC 7S TC 3C 5D +4C 2S AD QS 6C 9S QD TH QH 5C +8C AD QS 2D 2S KC JD KS 6C JC +8D 4D JS 2H 5D QD 7S 7D QH TS +6S 7H 3S 8C 8S 9D QS 8H 6C 9S +4S TC 2S 5C QD 4D QS 6D TH 6S +3S 5C 9D 6H 8D 4C 7D TC 7C TD +AH 6S AS 7H 5S KD 3H 5H AC 4C +8D 8S AH KS QS 2C AD 6H 7D 5D +6H 9H 9S 2H QS 8S 9C 5D 2D KD +TS QC 5S JH 7D 7S TH 9S 9H AC +7H 3H 6S KC 4D 6D 5C 4S QD TS +TD 2S 7C QD 3H JH 9D 4H 7S 7H +KS 3D 4H 5H TC 2S AS 2D 6D 7D +8H 3C 7H TD 3H AD KC TH 9C KH +TC 4C 2C 9S 9D 9C 5C 2H JD 3C +3H AC TS 5D AD 8D 6H QC 6S 8C +2S TS 3S JD 7H 8S QH 4C 5S 8D +AC 4S 6C 3C KH 3D 7C 2D 8S 2H +4H 6C 8S TH 2H 4S 8H 9S 3H 7S +7C 4C 9C 2C 5C AS 5D KD 4D QH +9H 4H TS AS 7D 8D 5D 9S 8C 2H +QC KD AC AD 2H 7S AS 3S 2D 9S +2H QC 8H TC 6D QD QS 5D KH 3C +TH JD QS 4C 2S 5S AD 7H 3S AS +7H JS 3D 6C 3S 6D AS 9S AC QS +9C TS AS 8C TC 8S 6H 9D 8D 6C +4D JD 9C KC 7C 6D KS 3S 8C AS +3H 6S TC 8D TS 3S KC 9S 7C AS +8C QC 4H 4S 8S 6C 3S TC AH AC +4D 7D 5C AS 2H 6S TS QC AD TC +QD QC 8S 4S TH 3D AH TS JH 4H +5C 2D 9S 2C 3H 3C 9D QD QH 7D +KC 9H 6C KD 7S 3C 4D AS TC 2D +3D JS 4D 9D KS 7D TH QC 3H 3C +8D 5S 2H 9D 3H 8C 4C 4H 3C TH +JC TH 4S 6S JD 2D 4D 6C 3D 4C +TS 3S 2D 4H AC 2C 6S 2H JH 6H +TD 8S AD TC AH AC JH 9S 6S 7S +6C KC 4S JD 8D 9H 5S 7H QH AH +KD 8D TS JH 5C 5H 3H AD AS JS +2D 4H 3D 6C 8C 7S AD 5D 5C 8S +TD 5D 7S 9C 4S 5H 6C 8C 4C 8S +JS QH 9C AS 5C QS JC 3D QC 7C +JC 9C KH JH QS QC 2C TS 3D AD +5D JH AC 5C 9S TS 4C JD 8C KS +KC AS 2D KH 9H 2C 5S 4D 3D 6H +TH AH 2D 8S JC 3D 8C QH 7S 3S +8H QD 4H JC AS KH KS 3C 9S 6D +9S QH 7D 9C 4S AC 7H KH 4D KD +AH AD TH 6D 9C 9S KD KS QH 4H +QD 6H 9C 7C QS 6D 6S 9D 5S JH +AH 8D 5H QD 2H JC KS 4H KH 5S +5C 2S JS 8D 9C 8C 3D AS KC AH +JD 9S 2H QS 8H 5S 8C TH 5C 4C +QC QS 8C 2S 2C 3S 9C 4C KS KH +2D 5D 8S AH AD TD 2C JS KS 8C +TC 5S 5H 8H QC 9H 6H JD 4H 9S +3C JH 4H 9H AH 4S 2H 4C 8D AC +8S TH 4D 7D 6D QD QS 7S TC 7C +KH 6D 2D JD 5H JS QD JH 4H 4S +9C 7S JH 4S 3S TS QC 8C TC 4H +QH 9D 4D JH QS 3S 2C 7C 6C 2D +4H 9S JD 5C 5H AH 9D TS 2D 4C +KS JH TS 5D 2D AH JS 7H AS 8D +JS AH 8C AD KS 5S 8H 2C 6C TH +2H 5D AD AC KS 3D 8H TS 6H QC +6D 4H TS 9C 5H JS JH 6S JD 4C +JH QH 4H 2C 6D 3C 5D 4C QS KC +6H 4H 6C 7H 6S 2S 8S KH QC 8C +3H 3D 5D KS 4H TD AD 3S 4D TS +5S 7C 8S 7D 2C KS 7S 6C 8C JS +5D 2H 3S 7C 5C QD 5H 6D 9C 9H +JS 2S KD 9S 8D TD TS AC 8C 9D +5H QD 2S AC 8C 9H KS 7C 4S 3C +KH AS 3H 8S 9C JS QS 4S AD 4D +AS 2S TD AD 4D 9H JC 4C 5H QS +5D 7C 4H TC 2D 6C JS 4S KC 3S +4C 2C 5D AC 9H 3D JD 8S QS QH +2C 8S 6H 3C QH 6D TC KD AC AH +QC 6C 3S QS 4S AC 8D 5C AD KH +5S 4C AC KH AS QC 2C 5C 8D 9C +8H JD 3C KH 8D 5C 9C QD QH 9D +7H TS 2C 8C 4S TD JC 9C 5H QH +JS 4S 2C 7C TH 6C AS KS 7S JD +JH 7C 9H 7H TC 5H 3D 6D 5D 4D +2C QD JH 2H 9D 5S 3D TD AD KS +JD QH 3S 4D TH 7D 6S QS KS 4H +TC KS 5S 8D 8H AD 2S 2D 4C JH +5S JH TC 3S 2D QS 9D 4C KD 9S +AC KH 3H AS 9D KC 9H QD 6C 6S +9H 7S 3D 5C 7D KC TD 8H 4H 6S +3C 7H 8H TC QD 4D 7S 6S QH 6C +6D AD 4C QD 6C 5D 7D 9D KS TS +JH 2H JD 9S 7S TS KH 8D 5D 8H +2D 9S 4C 7D 9D 5H QD 6D AC 6S +7S 6D JC QD JH 4C 6S QS 2H 7D +8C TD JH KD 2H 5C QS 2C JS 7S +TC 5H 4H JH QD 3S 5S 5D 8S KH +KS KH 7C 2C 5D JH 6S 9C 6D JC +5H AH JD 9C JS KC 2H 6H 4D 5S +AS 3C TH QC 6H 9C 8S 8C TD 7C +KC 2C QD 9C KH 4D 7S 3C TS 9H +9C QC 2S TS 8C TD 9S QD 3S 3C +4D 9D TH JH AH 6S 2S JD QH JS +QD 9H 6C KD 7D 7H 5D 6S 8H AH +8H 3C 4S 2H 5H QS QH 7S 4H AC +QS 3C 7S 9S 4H 3S AH KS 9D 7C +AD 5S 6S 2H 2D 5H TC 4S 3C 8C +QH TS 6S 4D JS KS JH AS 8S 6D +2C 8S 2S TD 5H AS TC TS 6C KC +KC TS 8H 2H 3H 7C 4C 5S TH TD +KD AD KH 7H 7S 5D 5H 5S 2D 9C +AD 9S 3D 7S 8C QC 7C 9C KD KS +3C QC 9S 8C 4D 5C AS QD 6C 2C +2H KC 8S JD 7S AC 8D 5C 2S 4D +9D QH 3D 2S TC 3S KS 3C 9H TD +KD 6S AC 2C 7H 5H 3S 6C 6H 8C +QH TC 8S 6S KH TH 4H 5D TS 4D +8C JS 4H 6H 2C 2H 7D AC QD 3D +QS KC 6S 2D 5S 4H TD 3H JH 4C +7S 5H 7H 8H KH 6H QS TH KD 7D +5H AD KD 7C KH 5S TD 6D 3C 6C +8C 9C 5H JD 7C KC KH 7H 2H 3S +7S 4H AD 4D 8S QS TH 3D 7H 5S +8D TC KS KD 9S 6D AD JD 5C 2S +7H 8H 6C QD 2H 6H 9D TC 9S 7C +8D 6D 4C 7C 6C 3C TH KH JS JH +5S 3S 8S JS 9H AS AD 8H 7S KD +JH 7C 2C KC 5H AS AD 9C 9S JS +AD AC 2C 6S QD 7C 3H TH KS KD +9D JD 4H 8H 4C KH 7S TS 8C KC +3S 5S 2H 7S 6H 7D KS 5C 6D AD +5S 8C 9H QS 7H 7S 2H 6C 7D TD +QS 5S TD AC 9D KC 3D TC 2D 4D +TD 2H 7D JD QD 4C 7H 5D KC 3D +4C 3H 8S KD QH 5S QC 9H TC 5H +9C QD TH 5H TS 5C 9H AH QH 2C +4D 6S 3C AC 6C 3D 2C 2H TD TH +AC 9C 5D QC 4D AD 8D 6D 8C KC +AD 3C 4H AC 8D 8H 7S 9S TD JC +4H 9H QH JS 2D TH TD TC KD KS +5S 6S 9S 8D TH AS KH 5H 5C 8S +JD 2S 9S 6S 5S 8S 5D 7S 7H 9D +5D 8C 4C 9D AD TS 2C 7D KD TC +8S QS 4D KC 5C 8D 4S KH JD KD +AS 5C AD QH 7D 2H 9S 7H 7C TC +2S 8S JD KH 7S 6C 6D AD 5D QC +9H 6H 3S 8C 8H AH TC 4H JS TD +2C TS 4D 7H 2D QC 9C 5D TH 7C +6C 8H QC 5D TS JH 5C 5H 9H 4S +2D QC 7H AS JS 8S 2H 4C 4H 8D +JS 6S AC KD 3D 3C 4S 7H TH KC +QH KH 6S QS 5S 4H 3C QD 3S 3H +7H AS KH 8C 4H 9C 5S 3D 6S TS +9C 7C 3H 5S QD 2C 3D AD AC 5H +JH TD 2D 4C TS 3H KH AD 3S 7S +AS 4C 5H 4D 6S KD JC 3C 6H 2D +3H 6S 8C 2D TH 4S AH QH AD 5H +7C 2S 9H 7H KC 5C 6D 5S 3H JC +3C TC 9C 4H QD TD JH 6D 9H 5S +7C 6S 5C 5D 6C 4S 7H 9H 6H AH +AD 2H 7D KC 2C 4C 2S 9S 7H 3S +TH 4C 8S 6S 3S AD KS AS JH TD +5C TD 4S 4D AD 6S 5D TC 9C 7D +8H 3S 4D 4S 5S 6H 5C AC 3H 3D +9H 3C AC 4S QS 8S 9D QH 5H 4D +JC 6C 5H TS AC 9C JD 8C 7C QD +8S 8H 9C JD 2D QC QH 6H 3C 8D +KS JS 2H 6H 5H QH QS 3H 7C 6D +TC 3H 4S 7H QC 2H 3S 8C JS KH +AH 8H 5S 4C 9H JD 3H 7S JC AC +3C 2D 4C 5S 6C 4S QS 3S JD 3D +5H 2D TC AH KS 6D 7H AD 8C 6H +6C 7S 3C JD 7C 8H KS KH AH 6D +AH 7D 3H 8H 8S 7H QS 5H 9D 2D +JD AC 4H 7S 8S 9S KS AS 9D QH +7S 2C 8S 5S JH QS JC AH KD 4C +AH 2S 9H 4H 8D TS TD 6H QH JD +4H JC 3H QS 6D 7S 9C 8S 9D 8D +5H TD 4S 9S 4C 8C 8D 7H 3H 3D +QS KH 3S 2C 2S 3C 7S TD 4S QD +7C TD 4D 5S KH AC AS 7H 4C 6C +2S 5H 6D JD 9H QS 8S 2C 2H TD +2S TS 6H 9H 7S 4H JC 4C 5D 5S +2C 5H 7D 4H 3S QH JC JS 6D 8H +4C QH 7C QD 3S AD TH 8S 5S TS +9H TC 2S TD JC 7D 3S 3D TH QH +7D 4C 8S 5C JH 8H 6S 3S KC 3H +JC 3H KH TC QH TH 6H 2C AC 5H +QS 2H 9D 2C AS 6S 6C 2S 8C 8S +9H 7D QC TH 4H KD QS AC 7S 3C +4D JH 6S 5S 8H KS 9S QC 3S AS +JD 2D 6S 7S TC 9H KC 3H 7D KD +2H KH 7C 4D 4S 3H JS QD 7D KC +4C JC AS 9D 3C JS 6C 8H QD 4D +AH JS 3S 6C 4C 3D JH 6D 9C 9H +9H 2D 8C 7H 5S KS 6H 9C 2S TC +6C 8C AD 7H 6H 3D KH AS 5D TH +KS 8C 3S TS 8S 4D 5S 9S 6C 4H +9H 4S 4H 5C 7D KC 2D 2H 9D JH +5C JS TC 9D 9H 5H 7S KH JC 6S +7C 9H 8H 4D JC KH JD 2H TD TC +8H 6C 2H 2C KH 6H 9D QS QH 5H +AC 7D 2S 3D QD JC 2D 8D JD JH +2H JC 2D 7H 2C 3C 8D KD TD 4H +3S 4H 6D 8D TS 3H TD 3D 6H TH +JH JC 3S AC QH 9H 7H 8S QC 2C +7H TD QS 4S 8S 9C 2S 5D 4D 2H +3D TS 3H 2S QC 8H 6H KC JC KS +5D JD 7D TC 8C 6C 9S 3D 8D AC +8H 6H JH 6C 5D 8D 8S 4H AD 2C +9D 4H 2D 2C 3S TS AS TC 3C 5D +4D TH 5H KS QS 6C 4S 2H 3D AD +5C KC 6H 2C 5S 3C 4D 2D 9H 9S +JD 4C 3H TH QH 9H 5S AH 8S AC +7D 9S 6S 2H TD 9C 4H 8H QS 4C +3C 6H 5D 4H 8C 9C KC 6S QD QS +3S 9H KD TC 2D JS 8C 6S 4H 4S +2S 4C 8S QS 6H KH 3H TH 8C 5D +2C KH 5S 3S 7S 7H 6C 9D QD 8D +8H KS AC 2D KH TS 6C JS KC 7H +9C KS 5C TD QC AH 6C 5H 9S 7C +5D 4D 3H 4H 6S 7C 7S AH QD TD +2H 7D QC 6S TC TS AH 7S 9D 3H +TH 5H QD 9S KS 7S 7C 6H 8C TD +TH 2D 4D QC 5C 7D JD AH 9C 4H +4H 3H AH 8D 6H QC QH 9H 2H 2C +2D AD 4C TS 6H 7S TH 4H QS TD +3C KD 2H 3H QS JD TC QC 5D 8H +KS JC QD TH 9S KD 8D 8C 2D 9C +3C QD KD 6D 4D 8D AH AD QC 8S +8H 3S 9D 2S 3H KS 6H 4C 7C KC +TH 9S 5C 3D 7D 6H AC 7S 4D 2C +5C 3D JD 4D 2D 6D 5H 9H 4C KH +AS 7H TD 6C 2H 3D QD KS 4C 4S +JC 3C AC 7C JD JS 8H 9S QC 5D +JD 6S 5S 2H AS 8C 7D 5H JH 3D +8D TC 5S 9S 8S 3H JC 5H 7S AS +5C TD 3D 7D 4H 8D 7H 4D 5D JS +QS 9C KS TD 2S 8S 5C 2H 4H AS +TH 7S 4H 7D 3H JD KD 5D 2S KC +JD 7H 4S 8H 4C JS 6H QH 5S 4H +2C QS 8C 5S 3H QC 2S 6C QD AD +8C 3D JD TC 4H 2H AD 5S AC 2S +5D 2C JS 2D AD 9D 3D 4C 4S JH +8D 5H 5D 6H 7S 4D KS 9D TD JD +3D 6D 9C 2S AS 7D 5S 5C 8H JD +7C 8S 3S 6S 5H JD TC AD 7H 7S +2S 9D TS 4D AC 8D 6C QD JD 3H +9S KH 2C 3C AC 3D 5H 6H 8D 5D +KS 3D 2D 6S AS 4C 2S 7C 7H KH +AC 2H 3S JC 5C QH 4D 2D 5H 7S +TS AS JD 8C 6H JC 8S 5S 2C 5D +7S QH 7H 6C QC 8H 2D 7C JD 2S +2C QD 2S 2H JC 9C 5D 2D JD JH +7C 5C 9C 8S 7D 6D 8D 6C 9S JH +2C AD 6S 5H 3S KS 7S 9D KH 4C +7H 6C 2C 5C TH 9D 8D 3S QC AH +5S KC 6H TC 5H 8S TH 6D 3C AH +9C KD 4H AD TD 9S 4S 7D 6H 5D +7H 5C 5H 6D AS 4C KD KH 4H 9D +3C 2S 5C 6C JD QS 2H 9D 7D 3H +AC 2S 6S 7S JS QD 5C QS 6H AD +5H TH QC 7H TC 3S 7C 6D KC 3D +4H 3D QC 9S 8H 2C 3S JC KS 5C +4S 6S 2C 6H 8S 3S 3D 9H 3H JS +4S 8C 4D 2D 8H 9H 7D 9D AH TS +9S 2C 9H 4C 8D AS 7D 3D 6D 5S +6S 4C 7H 8C 3H 5H JC AH 9D 9C +2S 7C 5S JD 8C 3S 3D 4D 7D 6S +3C KC 4S 5D 7D 3D JD 7H 3H 4H +9C 9H 4H 4D TH 6D QD 8S 9S 7S +2H AC 8S 4S AD 8C 2C AH 7D TC +TS 9H 3C AD KS TC 3D 8C 8H JD +QC 8D 2C 3C 7D 7C JD 9H 9C 6C +AH 6S JS JH 5D AS QC 2C JD TD +9H KD 2H 5D 2D 3S 7D TC AH TS +TD 8H AS 5D AH QC AC 6S TC 5H +KS 4S 7H 4D 8D 9C TC 2H 6H 3H +3H KD 4S QD QH 3D 8H 8C TD 7S +8S JD TC AH JS QS 2D KH KS 4D +3C AD JC KD JS KH 4S TH 9H 2C +QC 5S JS 9S KS AS 7C QD 2S JD +KC 5S QS 3S 2D AC 5D 9H 8H KS +6H 9C TC AD 2C 6D 5S JD 6C 7C +QS KH TD QD 2C 3H 8S 2S QC AH +9D 9H JH TC QH 3C 2S JS 5C 7H +6C 3S 3D 2S 4S QD 2D TH 5D 2C +2D 6H 6D 2S JC QH AS 7H 4H KH +5H 6S KS AD TC TS 7C AC 4S 4H +AD 3C 4H QS 8C 9D KS 2H 2D 4D +4S 9D 6C 6D 9C AC 8D 3H 7H KD +JC AH 6C TS JD 6D AD 3S 5D QD +JC JH JD 3S 7S 8S JS QC 3H 4S +JD TH 5C 2C AD JS 7H 9S 2H 7S +8D 3S JH 4D QC AS JD 2C KC 6H +2C AC 5H KD 5S 7H QD JH AH 2D +JC QH 8D 8S TC 5H 5C AH 8C 6C +3H JS 8S QD JH 3C 4H 6D 5C 3S +6D 4S 4C AH 5H 5S 3H JD 7C 8D +8H AH 2H 3H JS 3C 7D QC 4H KD +6S 2H KD 5H 8H 2D 3C 8S 7S QD +2S 7S KC QC AH TC QS 6D 4C 8D +5S 9H 2C 3S QD 7S 6C 2H 7C 9D +3C 6C 5C 5S JD JC KS 3S 5D TS +7C KS 6S 5S 2S 2D TC 2H 5H QS +AS 7H 6S TS 5H 9S 9D 3C KD 2H +4S JS QS 3S 4H 7C 2S AC 6S 9D +8C JH 2H 5H 7C 5D QH QS KH QC +3S TD 3H 7C KC 8D 5H 8S KH 8C +4H KH JD TS 3C 7H AS QC JS 5S +AH 9D 2C 8D 4D 2D 6H 6C KC 6S +2S 6H 9D 3S 7H 4D KH 8H KD 3D +9C TC AC JH KH 4D JD 5H TD 3S +7S 4H 9D AS 4C 7D QS 9S 2S KH +3S 8D 8S KS 8C JC 5C KH 2H 5D +8S QH 2C 4D KC JS QC 9D AC 6H +8S 8C 7C JS JD 6S 4C 9C AC 4S +QH 5D 2C 7D JC 8S 2D JS JH 4C +JS 4C 7S TS JH KC KH 5H QD 4S +QD 8C 8D 2D 6S TD 9D AC QH 5S +QH QC JS 3D 3C 5C 4H KH 8S 7H +7C 2C 5S JC 8S 3H QC 5D 2H KC +5S 8D KD 6H 4H QD QH 6D AH 3D +7S KS 6C 2S 4D AC QS 5H TS JD +7C 2D TC 5D QS AC JS QC 6C KC +2C KS 4D 3H TS 8S AD 4H 7S 9S +QD 9H QH 5H 4H 4D KH 3S JC AD +4D AC KC 8D 6D 4C 2D KH 2C JD +2C 9H 2D AH 3H 6D 9C 7D TC KS +8C 3H KD 7C 5C 2S 4S 5H AS AH +TH JD 4H KD 3H TC 5C 3S AC KH +6D 7H AH 7S QC 6H 2D TD JD AS +JH 5D 7H TC 9S 7D JC AS 5S KH +2H 8C AD TH 6H QD KD 9H 6S 6C +QH KC 9D 4D 3S JS JH 4H 2C 9H +TC 7H KH 4H JC 7D 9S 3H QS 7S +AD 7D JH 6C 7H 4H 3S 3H 4D QH +JD 2H 5C AS 6C QC 4D 3C TC JH +AC JD 3H 6H 4C JC AD 7D 7H 9H +4H TC TS 2C 8C 6S KS 2H JD 9S +4C 3H QS QC 9S 9H 6D KC 9D 9C +5C AD 8C 2C QH TH QD JC 8D 8H +QC 2C 2S QD 9C 4D 3S 8D JH QS +9D 3S 2C 7S 7C JC TD 3C TC 9H +3C TS 8H 5C 4C 2C 6S 8D 7C 4H +KS 7H 2H TC 4H 2C 3S AS AH QS +8C 2D 2H 2C 4S 4C 6S 7D 5S 3S +TH QC 5D TD 3C QS KD KC KS AS +4D AH KD 9H KS 5C 4C 6H JC 7S +KC 4H 5C QS TC 2H JC 9S AH QH +4S 9H 3H 5H 3C QD 2H QC JH 8H +5D AS 7H 2C 3D JH 6H 4C 6S 7D +9C JD 9H AH JS 8S QH 3H KS 8H +3S AC QC TS 4D AD 3D AH 8S 9H +7H 3H QS 9C 9S 5H JH JS AH AC +8D 3C JD 2H AC 9C 7H 5S 4D 8H +7C JH 9H 6C JS 9S 7H 8C 9D 4H +2D AS 9S 6H 4D JS JH 9H AD QD +6H 7S JH KH AH 7H TD 5S 6S 2C +8H JH 6S 5H 5S 9D TC 4C QC 9S +7D 2C KD 3H 5H AS QD 7H JS 4D +TS QH 6C 8H TH 5H 3C 3H 9C 9D +AD KH JS 5D 3H AS AC 9S 5C KC +2C KH 8C JC QS 6D AH 2D KC TC +9D 3H 2S 7C 4D 6D KH KS 8D 7D +9H 2S TC JH AC QC 3H 5S 3S 8H +3S AS KD 8H 4C 3H 7C JH QH TS +7S 6D 7H 9D JH 4C 3D 3S 6C AS +4S 2H 2C 4C 8S 5H KC 8C QC QD +3H 3S 6C QS QC 2D 6S 5D 2C 9D +2H 8D JH 2S 3H 2D 6C 5C 7S AD +9H JS 5D QH 8S TS 2H 7S 6S AD +6D QC 9S 7H 5H 5C 7D KC JD 4H +QC 5S 9H 9C 4D 6S KS 2S 4C 7C +9H 7C 4H 8D 3S 6H 5C 8H JS 7S +2D 6H JS TD 4H 4D JC TH 5H KC +AC 7C 8D TH 3H 9S 2D 4C KC 4D +KD QS 9C 7S 3D KS AD TS 4C 4H +QH 9C 8H 2S 7D KS 7H 5D KD 4C +9C 2S 2H JC 6S 6C TC QC JH 5C +7S AC 8H KC 8S 6H QS JC 3D 6S +JS 2D JH 8C 4S 6H 8H 6D 5D AD +6H 7D 2S 4H 9H 7C AS AC 8H 5S +3C JS 4S 6D 5H 2S QH 6S 9C 2C +3D 5S 6S 9S 4C QS 8D QD 8S TC +9C 3D AH 9H 5S 2C 7D AD JC 3S +7H TC AS 3C 6S 6D 7S KH KC 9H +3S TC 8H 6S 5H JH 8C 7D AC 2S +QD 9D 9C 3S JC 8C KS 8H 5D 4D +JS AH JD 6D 9D 8C 9H 9S 8H 3H +2D 6S 4C 4D 8S AD 4S TC AH 9H +TS AC QC TH KC 6D 4H 7S 8C 2H +3C QD JS 9D 5S JC AH 2H TS 9H +3H 4D QH 5D 9C 5H 7D 4S JC 3S +8S TH 3H 7C 2H JD JS TS AC 8D +9C 2H TD KC JD 2S 8C 5S AD 2C +3D KD 7C 5H 4D QH QD TC 6H 7D +7H 2C KC 5S KD 6H AH QC 7S QH +6H 5C AC 5H 2C 9C 2D 7C TD 2S +4D 9D AH 3D 7C JD 4H 8C 4C KS +TH 3C JS QH 8H 4C AS 3D QS QC +4D 7S 5H JH 6D 7D 6H JS KH 3C +QD 8S 7D 2H 2C 7C JC 2S 5H 8C +QH 8S 9D TC 2H AD 7C 8D QD 6S +3S 7C AD 9H 2H 9S JD TS 4C 2D +3S AS 4H QC 2C 8H 8S 7S TD TC +JH TH TD 3S 4D 4H 5S 5D QS 2C +8C QD QH TC 6D 4S 9S 9D 4H QC +8C JS 9D 6H JD 3H AD 6S TD QC +KC 8S 3D 7C TD 7D 8D 9H 4S 3S +6C 4S 3D 9D KD TC KC KS AC 5S +7C 6S QH 3D JS KD 6H 6D 2D 8C +JD 2S 5S 4H 8S AC 2D 6S TS 5C +5H 8C 5S 3C 4S 3D 7C 8D AS 3H +AS TS 7C 3H AD 7D JC QS 6C 6H +3S 9S 4C AC QH 5H 5D 9H TS 4H +6C 5C 7H 7S TD AD JD 5S 2H 2S +7D 6C KC 3S JD 8D 8S TS QS KH +8S QS 8D 6C TH AC AH 2C 8H 9S +7H TD KH QH 8S 3D 4D AH JD AS +TS 3D 2H JC 2S JH KH 6C QC JS +KC TH 2D 6H 7S 2S TC 8C 9D QS +3C 9D 6S KH 8H 6D 5D TH 2C 2H +6H TC 7D AD 4D 8S TS 9H TD 7S +JS 6D JD JC 2H AC 6C 3D KH 8D +KH JD 9S 5D 4H 4C 3H 7S QS 5C +4H JD 5D 3S 3C 4D KH QH QS 7S +JD TS 8S QD AH 4C 6H 3S 5S 2C +QS 3D JD AS 8D TH 7C 6S QC KS +7S 2H 8C QC 7H AC 6D 2D TH KH +5S 6C 7H KH 7D AH 8C 5C 7S 3D +3C KD AD 7D 6C 4D KS 2D 8C 4S +7C 8D 5S 2D 2S AH AD 2C 9D TD +3C AD 4S KS JH 7C 5C 8C 9C TH +AS TD 4D 7C JD 8C QH 3C 5H 9S +3H 9C 8S 9S 6S QD KS AH 5H JH +QC 9C 5S 4H 2H TD 7D AS 8C 9D +8C 2C 9D KD TC 7S 3D KH QC 3C +4D AS 4C QS 5S 9D 6S JD QH KS +6D AH 6C 4C 5H TS 9H 7D 3D 5S +QS JD 7C 8D 9C AC 3S 6S 6C KH +8H JH 5D 9S 6D AS 6S 3S QC 7H +QD AD 5C JH 2H AH 4H AS KC 2C +JH 9C 2C 6H 2D JS 5D 9H KC 6D +7D 9D KD TH 3H AS 6S QC 6H AD +JD 4H 7D KC 3H JS 3C TH 3D QS +4C 3H 8C QD 5H 6H AS 8H AD JD +TH 8S KD 5D QC 7D JS 5S 5H TS +7D KC 9D QS 3H 3C 6D TS 7S AH +7C 4H 7H AH QC AC 4D 5D 6D TH +3C 4H 2S KD 8H 5H JH TC 6C JD +4S 8C 3D 4H JS TD 7S JH QS KD +7C QC KD 4D 7H 6S AD TD TC KH +5H 9H KC 3H 4D 3D AD 6S QD 6H +TH 7C 6H TS QH 5S 2C KC TD 6S +7C 4D 5S JD JH 7D AC KD KH 4H +7D 6C 8D 8H 5C JH 8S QD TH JD +8D 7D 6C 7C 9D KD AS 5C QH JH +9S 2C 8C 3C 4C KS JH 2D 8D 4H +7S 6C JH KH 8H 3H 9D 2D AH 6D +4D TC 9C 8D 7H TD KS TH KD 3C +JD 9H 8D QD AS KD 9D 2C 2S 9C +8D 3H 5C 7H KS 5H QH 2D 8C 9H +2D TH 6D QD 6C KC 3H 3S AD 4C +4H 3H JS 9D 3C TC 5H QH QC JC +3D 5C 6H 3S 3C JC 5S 7S 2S QH +AC 5C 8C 4D 5D 4H 2S QD 3C 3H +2C TD AH 9C KD JS 6S QD 4C QC +QS 8C 3S 4H TC JS 3H 7C JC AD +5H 4D 9C KS JC TD 9S TS 8S 9H +QD TS 7D AS AC 2C TD 6H 8H AH +6S AD 8C 4S 9H 8D 9D KH 8S 3C +QS 4D 2D 7S KH JS JC AD 4C 3C +QS 9S 7H KC TD TH 5H JS AC JH +6D AC 2S QS 7C AS KS 6S KH 5S +6D 8H KH 3C QS 2H 5C 9C 9D 6C +JS 2C 4C 6H 7D JC AC QD TD 3H +4H QC 8H JD 4C KD KS 5C KC 7S +6D 2D 3H 2S QD 5S 7H AS TH 6S +AS 6D 8D 2C 8S TD 8H QD JC AH +9C 9H 2D TD QH 2H 5C TC 3D 8H +KC 8S 3D KH 2S TS TC 6S 4D JH +9H 9D QS AC KC 6H 5D 4D 8D AH +9S 5C QS 4H 7C 7D 2H 8S AD JS +3D AC 9S AS 2C 2D 2H 3H JC KH +7H QH KH JD TC KS 5S 8H 4C 8D +2H 7H 3S 2S 5H QS 3C AS 9H KD +AD 3D JD 6H 5S 9C 6D AC 9S 3S +3D 5D 9C 2D AC 4S 2S AD 6C 6S +QC 4C 2D 3H 6S KC QH QD 2H JH +QC 3C 8S 4D 9S 2H 5C 8H QS QD +6D KD 6S 7H 3S KH 2H 5C JC 6C +3S 9S TC 6S 8H 2D AD 7S 8S TS +3C 6H 9C 3H 5C JC 8H QH TD QD +3C JS QD 5D TD 2C KH 9H TH AS +9S TC JD 3D 5C 5H AD QH 9H KC +TC 7H 4H 8H 3H TD 6S AC 7C 2S +QS 9D 5D 3C JC KS 4D 6C JH 2S +9S 6S 3C 7H TS 4C KD 6D 3D 9C +2D 9H AH AC 7H 2S JH 3S 7C QC +QD 9H 3C 2H AC AS 8S KD 8C KH +2D 7S TD TH 6D JD 8D 4D 2H 5S +8S QH KD JD QS JH 4D KC 5H 3S +3C KH QC 6D 8H 3S AH 7D TD 2D +5S 9H QH 4S 6S 6C 6D TS TH 7S +6C 4C 6D QS JS 9C TS 3H 8D 8S +JS 5C 7S AS 2C AH 2H AD 5S TC +KD 6C 9C 9D TS 2S JC 4H 2C QD +QS 9H TC 3H KC KS 4H 3C AD TH +KH 9C 2H KD 9D TC 7S KC JH 2D +7C 3S KC AS 8C 5D 9C 9S QH 3H +2D 8C TD 4C 2H QC 5D TC 2C 7D +KS 4D 6C QH TD KH 5D 7C AD 8D +2S 9S 8S 4C 8C 3D 6H QD 7C 7H +6C 8S QH 5H TS 5C 3C 4S 2S 2H +8S 6S 2H JC 3S 3H 9D 8C 2S 7H +QC 2C 8H 9C AC JD 4C 4H 6S 3S +3H 3S 7D 4C 9S 5H 8H JC 3D TC +QH 2S 2D 9S KD QD 9H AD 6D 9C +8D 2D KS 9S JC 4C JD KC 4S TH +KH TS 6D 4D 5C KD 5H AS 9H AD +QD JS 7C 6D 5D 5C TH 5H QH QS +9D QH KH 5H JH 4C 4D TC TH 6C +KH AS TS 9D KD 9C 7S 4D 8H 5S +KH AS 2S 7D 9D 4C TS TH AH 7C +KS 4D AC 8S 9S 8D TH QH 9D 5C +5D 5C 8C QS TC 4C 3D 3S 2C 8D +9D KS 2D 3C KC 4S 8C KH 6C JC +8H AH 6H 7D 7S QD 3C 4C 6C KC +3H 2C QH 8H AS 7D 4C 8C 4H KC +QD 5S 4H 2C TD AH JH QH 4C 8S +3H QS 5S JS 8H 2S 9H 9C 3S 2C +6H TS 7S JC QD AC TD KC 5S 3H +QH AS QS 7D JC KC 2C 4C 5C 5S +QH 3D AS JS 4H 8D 7H JC 2S 9C +5D 4D 2S 4S 9D 9C 2D QS 8H 7H +6D 7H 3H JS TS AC 2D JH 7C 8S +JH 5H KC 3C TC 5S 9H 4C 8H 9D +8S KC 5H 9H AD KS 9D KH 8D AH +JC 2H 9H KS 6S 3H QC 5H AH 9C +5C KH 5S AD 6C JC 9H QC 9C TD +5S 5D JC QH 2D KS 8H QS 2H TS +JH 5H 5S AH 7H 3C 8S AS TD KH +6H 3D JD 2C 4C KC 7S AH 6C JH +4C KS 9D AD 7S KC 7D 8H 3S 9C +7H 5C 5H 3C 8H QC 3D KH 6D JC +2D 4H 5D 7D QC AD AH 9H QH 8H +KD 8C JS 9D 3S 3C 2H 5D 6D 2S +8S 6S TS 3C 6H 8D 5S 3H TD 6C +KS 3D JH 9C 7C 9S QS 5S 4H 6H +7S 6S TH 4S KC KD 3S JC JH KS +7C 3C 2S 6D QH 2C 7S 5H 8H AH +KC 8D QD 6D KH 5C 7H 9D 3D 9C +6H 2D 8S JS 9S 2S 6D KC 7C TC +KD 9C JH 7H KC 8S 2S 7S 3D 6H +4H 9H 2D 4C 8H 7H 5S 8S 2H 8D +AD 7C 3C 7S 5S 4D 9H 3D JC KH +5D AS 7D 6D 9C JC 4C QH QS KH +KD JD 7D 3D QS QC 8S 6D JS QD +6S 8C 5S QH TH 9H AS AC 2C JD +QC KS QH 7S 3C 4C 5C KC 5D AH +6C 4H 9D AH 2C 3H KD 3D TS 5C +TD 8S QS AS JS 3H KD AC 4H KS +7D 5D TS 9H 4H 4C 9C 2H 8C QC +2C 7D 9H 4D KS 4C QH AD KD JS +QD AD AH KH 9D JS 9H JC KD JD +8S 3C 4S TS 7S 4D 5C 2S 6H 7C +JS 7S 5C KD 6D QH 8S TD 2H 6S +QH 6C TC 6H TD 4C 9D 2H QC 8H +3D TS 4D 2H 6H 6S 2C 7H 8S 6C +9H 9D JD JH 3S AH 2C 6S 3H 8S +2C QS 8C 5S 3H 2S 7D 3C AD 4S +5C QC QH AS TS 4S 6S 4C 5H JS +JH 5C TD 4C 6H JS KD KH QS 4H +TC KH JC 4D 9H 9D 8D KC 3C 8H +2H TC 8S AD 9S 4H TS 7H 2C 5C +4H 2S 6C 5S KS AH 9C 7C 8H KD +TS QH TD QS 3C JH AH 2C 8D 7D +5D KC 3H 5S AC 4S 7H QS 4C 2H +3D 7D QC KH JH 6D 6C TD TH KD +5S 8D TH 6C 9D 7D KH 8C 9S 6D +JD QS 7S QC 2S QH JC 4S KS 8D +7S 5S 9S JD KD 9C JC AD 2D 7C +4S 5H AH JH 9C 5D TD 7C 2D 6S +KC 6C 7H 6S 9C QD 5S 4H KS TD +6S 8D KS 2D TH TD 9H JD TS 3S +KH JS 4H 5D 9D TC TD QC JD TS +QS QD AC AD 4C 6S 2D AS 3H KC +4C 7C 3C TD QS 9C KC AS 8D AD +KC 7H QC 6D 8H 6S 5S AH 7S 8C +3S AD 9H JC 6D JD AS KH 6S JH +AD 3D TS KS 7H JH 2D JS QD AC +9C JD 7C 6D TC 6H 6C JC 3D 3S +QC KC 3S JC KD 2C 8D AH QS TS +AS KD 3D JD 8H 7C 8C 5C QD 6C diff --git a/project_euler/problem_54/sol1.py b/project_euler/problem_54/sol1.py new file mode 100644 index 000000000000..4d75271784de --- /dev/null +++ b/project_euler/problem_54/sol1.py @@ -0,0 +1,381 @@ +""" +Problem: https://projecteuler.net/problem=54 + +In the card game poker, a hand consists of five cards and are ranked, +from lowest to highest, in the following way: + +High Card: Highest value card. +One Pair: Two cards of the same value. +Two Pairs: Two different pairs. +Three of a Kind: Three cards of the same value. +Straight: All cards are consecutive values. +Flush: All cards of the same suit. +Full House: Three of a kind and a pair. +Four of a Kind: Four cards of the same value. +Straight Flush: All cards are consecutive values of same suit. +Royal Flush: Ten, Jack, Queen, King, Ace, in same suit. + +The cards are valued in the order: +2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace. + +If two players have the same ranked hands then the rank made up of the highest +value wins; for example, a pair of eights beats a pair of fives. +But if two ranks tie, for example, both players have a pair of queens, then highest +cards in each hand are compared; if the highest cards tie then the next highest +cards are compared, and so on. + +The file, poker.txt, contains one-thousand random hands dealt to two players. +Each line of the file contains ten cards (separated by a single space): the +first five are Player 1's cards and the last five are Player 2's cards. +You can assume that all hands are valid (no invalid characters or repeated cards), +each player's hand is in no specific order, and in each hand there is a clear winner. + +How many hands does Player 1 win? + +Resources used: +https://en.wikipedia.org/wiki/Texas_hold_%27em +https://en.wikipedia.org/wiki/List_of_poker_hands + +Similar problem on codewars: +https://www.codewars.com/kata/ranking-poker-hands +https://www.codewars.com/kata/sortable-poker-hands +""" +from __future__ import annotations + +import os + + +class PokerHand(object): + """Create an object representing a Poker Hand based on an input of a + string which represents the best 5 card combination from the player's hand + and board cards. + + Attributes: (read-only) + hand: string representing the hand consisting of five cards + + Methods: + compare_with(opponent): takes in player's hand (self) and + opponent's hand (opponent) and compares both hands according to + the rules of Texas Hold'em. + Returns one of 3 strings (Win, Loss, Tie) based on whether + player's hand is better than opponent's hand. + + hand_name(): Returns a string made up of two parts: hand name + and high card. + + Supported operators: + Rich comparison operators: <, >, <=, >=, ==, != + + Supported builtin methods and functions: + list.sort(), sorted() + """ + + _HAND_NAME = [ + "High card", + "One pair", + "Two pairs", + "Three of a kind", + "Straight", + "Flush", + "Full house", + "Four of a kind", + "Straight flush", + "Royal flush", + ] + + _CARD_NAME = [ + "", # placeholder as lists are zero indexed + "One", + "Two", + "Three", + "Four", + "Five", + "Six", + "Seven", + "Eight", + "Nine", + "Ten", + "Jack", + "Queen", + "King", + "Ace", + ] + + def __init__(self, hand: str) -> None: + """ + Initialize hand. + Hand should of type str and should contain only five cards each + separated by a space. + + The cards should be of the following format: + [card value][card suit] + + The first character is the value of the card: + 2, 3, 4, 5, 6, 7, 8, 9, T(en), J(ack), Q(ueen), K(ing), A(ce) + + The second character represents the suit: + S(pades), H(earts), D(iamonds), C(lubs) + + For example: "6S 4C KC AS TH" + """ + if not isinstance(hand, str): + raise TypeError(f"Hand should be of type 'str': {hand!r}") + # split removes duplicate whitespaces so no need of strip + if len(hand.split(" ")) != 5: + raise ValueError(f"Hand should contain only 5 cards: {hand!r}") + self._hand = hand + self._first_pair = 0 + self._second_pair = 0 + self._card_values, self._card_suit = self._internal_state() + self._hand_type = self._get_hand_type() + self._high_card = self._card_values[0] + + @property + def hand(self): + """Returns the self hand""" + return self._hand + + def compare_with(self, other: "PokerHand") -> str: + """ + Determines the outcome of comparing self hand with other hand. + Returns the output as 'Win', 'Loss', 'Tie' according to the rules of + Texas Hold'em. + + Here are some examples: + >>> player = PokerHand("2H 3H 4H 5H 6H") # Stright flush + >>> opponent = PokerHand("KS AS TS QS JS") # Royal flush + >>> player.compare_with(opponent) + 'Loss' + + >>> player = PokerHand("2S AH 2H AS AC") # Full house + >>> opponent = PokerHand("2H 3H 5H 6H 7H") # Flush + >>> player.compare_with(opponent) + 'Win' + + >>> player = PokerHand("2S AH 4H 5S 6C") # High card + >>> opponent = PokerHand("AD 4C 5H 6H 2C") # High card + >>> player.compare_with(opponent) + 'Tie' + """ + # Breaking the tie works on the following order of precedence: + # 1. First pair (default 0) + # 2. Second pair (default 0) + # 3. Compare all cards in reverse order because they are sorted. + + # First pair and second pair will only be a non-zero value if the card + # type is either from the following: + # 21: Four of a kind + # 20: Full house + # 17: Three of a kind + # 16: Two pairs + # 15: One pair + if self._hand_type > other._hand_type: + return "Win" + elif self._hand_type < other._hand_type: + return "Loss" + elif self._first_pair == other._first_pair: + if self._second_pair == other._second_pair: + return self._compare_cards(other) + else: + return "Win" if self._second_pair > other._second_pair else "Loss" + return "Win" if self._first_pair > other._first_pair else "Loss" + + # This function is not part of the problem, I did it just for fun + def hand_name(self) -> str: + """ + Return the name of the hand in the following format: + 'hand name, high card' + + Here are some examples: + >>> PokerHand("KS AS TS QS JS").hand_name() + 'Royal flush' + + >>> PokerHand("2D 6D 3D 4D 5D").hand_name() + 'Straight flush, Six-high' + + >>> PokerHand("JC 6H JS JD JH").hand_name() + 'Four of a kind, Jacks' + + >>> PokerHand("3D 2H 3H 2C 2D").hand_name() + 'Full house, Twos over Threes' + + >>> PokerHand("2H 4D 3C AS 5S").hand_name() # Low ace + 'Straight, Five-high' + + Source: https://en.wikipedia.org/wiki/List_of_poker_hands + """ + name = PokerHand._HAND_NAME[self._hand_type - 14] + high = PokerHand._CARD_NAME[self._high_card] + pair1 = PokerHand._CARD_NAME[self._first_pair] + pair2 = PokerHand._CARD_NAME[self._second_pair] + if self._hand_type in [22, 19, 18]: + return name + f", {high}-high" + elif self._hand_type in [21, 17, 15]: + return name + f", {pair1}s" + elif self._hand_type in [20, 16]: + join = "over" if self._hand_type == 20 else "and" + return name + f", {pair1}s {join} {pair2}s" + elif self._hand_type == 23: + return name + else: + return name + f", {high}" + + def _compare_cards(self, other: "PokerHand") -> str: + # Enumerate gives us the index as well as the element of a list + for index, card_value in enumerate(self._card_values): + if card_value != other._card_values[index]: + return "Win" if card_value > other._card_values[index] else "Loss" + return "Tie" + + def _get_hand_type(self) -> int: + # Number representing the type of hand internally: + # 23: Royal flush + # 22: Straight flush + # 21: Four of a kind + # 20: Full house + # 19: Flush + # 18: Straight + # 17: Three of a kind + # 16: Two pairs + # 15: One pair + # 14: High card + if self._is_flush(): + if self._is_five_high_straight() or self._is_straight(): + return 23 if sum(self._card_values) == 60 else 22 + return 19 + elif self._is_five_high_straight() or self._is_straight(): + return 18 + return 14 + self._is_same_kind() + + def _is_flush(self) -> bool: + return len(self._card_suit) == 1 + + def _is_five_high_straight(self) -> bool: + # If a card is a five high straight (low ace) change the location of + # ace from the start of the list to the end. Check whether the first + # element is ace or not. (Don't want to change again) + # Five high straight (low ace): AH 2H 3S 4C 5D + # Why use sorted here? One call to this function will mutate the list to + # [5, 4, 3, 2, 14] and so for subsequent calls (which will be rare) we + # need to compare the sorted version. + # Refer test_multiple_calls_five_high_straight in test_poker_hand.py + if sorted(self._card_values) == [2, 3, 4, 5, 14]: + if self._card_values[0] == 14: + # Remember, our list is sorted in reverse order + ace_card = self._card_values.pop(0) + self._card_values.append(ace_card) + return True + return False + + def _is_straight(self) -> bool: + for i in range(4): + if self._card_values[i] - self._card_values[i + 1] != 1: + return False + return True + + def _is_same_kind(self) -> int: + # Kind Values for internal use: + # 7: Four of a kind + # 6: Full house + # 3: Three of a kind + # 2: Two pairs + # 1: One pair + # 0: False + kind = val1 = val2 = 0 + for i in range(4): + # Compare two cards at a time, if they are same increase 'kind', + # add the value of the card to val1, if it is repeating again we + # will add 2 to 'kind' as there are now 3 cards with same value. + # If we get card of different value than val1, we will do the same + # thing with val2 + if self._card_values[i] == self._card_values[i + 1]: + if not val1: + val1 = self._card_values[i] + kind += 1 + elif val1 == self._card_values[i]: + kind += 2 + elif not val2: + val2 = self._card_values[i] + kind += 1 + elif val2 == self._card_values[i]: + kind += 2 + # For consistency in hand type (look at note in _get_hand_type function) + kind = kind + 2 if kind in [4, 5] else kind + # first meaning first pair to compare in 'compare_with' + first = max(val1, val2) + second = min(val1, val2) + # If it's full house (three count pair + two count pair), make sure + # first pair is three count and if not then switch them both. + if kind == 6 and self._card_values.count(first) != 3: + first, second = second, first + self._first_pair = first + self._second_pair = second + return kind + + def _internal_state(self) -> tuple[list[int], set[str]]: + # Internal representation of hand as a list of card values and + # a set of card suit + trans: dict = {"T": "10", "J": "11", "Q": "12", "K": "13", "A": "14"} + new_hand = self._hand.translate(str.maketrans(trans)).split() + card_values = [int(card[:-1]) for card in new_hand] + card_suit = {card[-1] for card in new_hand} + return sorted(card_values, reverse=True), card_suit + + def __repr__(self): + return f'{self.__class__}("{self._hand}")' + + def __str__(self): + return self._hand + + # Rich comparison operators (used in list.sort() and sorted() builtin functions) + # Note that this is not part of the problem but another extra feature where + # if you have a list of PokerHand objects, you can sort them just through + # the builtin functions. + def __eq__(self, other): + if isinstance(other, PokerHand): + return self.compare_with(other) == "Tie" + return NotImplemented + + def __lt__(self, other): + if isinstance(other, PokerHand): + return self.compare_with(other) == "Loss" + return NotImplemented + + def __le__(self, other): + if isinstance(other, PokerHand): + return self < other or self == other + return NotImplemented + + def __gt__(self, other): + if isinstance(other, PokerHand): + return not self < other and self != other + return NotImplemented + + def __ge__(self, other): + if isinstance(other, PokerHand): + return not self < other + return NotImplemented + + def __hash__(self): + return object.__hash__(self) + + +def solution() -> int: + # Solution for problem number 54 from Project Euler + # Input from poker_hands.txt file + answer = 0 + script_dir = os.path.abspath(os.path.dirname(__file__)) + poker_hands = os.path.join(script_dir, "poker_hands.txt") + with open(poker_hands, "r") as file_hand: + for line in file_hand: + player_hand = line[:14].strip() + opponent_hand = line[15:].strip() + player, opponent = PokerHand(player_hand), PokerHand(opponent_hand) + output = player.compare_with(opponent) + if output == "Win": + answer += 1 + return answer + + +if __name__ == "__main__": + solution() diff --git a/project_euler/problem_54/test_poker_hand.py b/project_euler/problem_54/test_poker_hand.py new file mode 100644 index 000000000000..f60c3aba6616 --- /dev/null +++ b/project_euler/problem_54/test_poker_hand.py @@ -0,0 +1,228 @@ +import os +from itertools import chain +from random import randrange, shuffle + +import pytest + +from .sol1 import PokerHand + +SORTED_HANDS = ( + "4S 3H 2C 7S 5H", + "9D 8H 2C 6S 7H", + "2D 6D 9D TH 7D", + "TC 8C 2S JH 6C", + "JH 8S TH AH QH", + "TS KS 5S 9S AC", + "KD 6S 9D TH AD", + "KS 8D 4D 9S 4S", # pair + "8C 4S KH JS 4D", # pair + "QH 8H KD JH 8S", # pair + "KC 4H KS 2H 8D", # pair + "KD 4S KC 3H 8S", # pair + "AH 8S AS KC JH", # pair + "3H 4C 4H 3S 2H", # 2 pairs + "5S 5D 2C KH KH", # 2 pairs + "3C KH 5D 5S KH", # 2 pairs + "AS 3C KH AD KH", # 2 pairs + "7C 7S 3S 7H 5S", # 3 of a kind + "7C 7S KH 2H 7H", # 3 of a kind + "AC KH QH AH AS", # 3 of a kind + "2H 4D 3C AS 5S", # straight (low ace) + "3C 5C 4C 2C 6H", # straight + "6S 8S 7S 5H 9H", # straight + "JS QS 9H TS KH", # straight + "QC KH TS JS AH", # straight (high ace) + "8C 9C 5C 3C TC", # flush + "3S 8S 9S 5S KS", # flush + "4C 5C 9C 8C KC", # flush + "JH 8H AH KH QH", # flush + "3D 2H 3H 2C 2D", # full house + "2H 2C 3S 3H 3D", # full house + "KH KC 3S 3H 3D", # full house + "JC 6H JS JD JH", # 4 of a kind + "JC 7H JS JD JH", # 4 of a kind + "JC KH JS JD JH", # 4 of a kind + "2S AS 4S 5S 3S", # straight flush (low ace) + "2D 6D 3D 4D 5D", # straight flush + "5C 6C 3C 7C 4C", # straight flush + "JH 9H TH KH QH", # straight flush + "JH AH TH KH QH", # royal flush (high ace straight flush) +) + +TEST_COMPARE = ( + ("2H 3H 4H 5H 6H", "KS AS TS QS JS", "Loss"), + ("2H 3H 4H 5H 6H", "AS AD AC AH JD", "Win"), + ("AS AH 2H AD AC", "JS JD JC JH 3D", "Win"), + ("2S AH 2H AS AC", "JS JD JC JH AD", "Loss"), + ("2S AH 2H AS AC", "2H 3H 5H 6H 7H", "Win"), + ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", "Win"), + ("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C", "Win"), + ("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S", "Tie"), + ("2S 3H 4H 5S 6C", "AH AC 5H 6H AS", "Win"), + ("2S 2H 4H 5S 4C", "AH AC 5H 6H AS", "Loss"), + ("2S 2H 4H 5S 4C", "AH AC 5H 6H 7S", "Win"), + ("6S AD 7H 4S AS", "AH AC 5H 6H 7S", "Loss"), + ("2S AH 4H 5S KC", "AH AC 5H 6H 7S", "Loss"), + ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", "Loss"), + ("4S 5H 6H TS AC", "3S 5H 6H TS AC", "Win"), + ("2S AH 4H 5S 6C", "AD 4C 5H 6H 2C", "Tie"), + ("AS AH 3H AD AC", "AS AH 2H AD AC", "Win"), + ("AH AC 5H 5C QS", "AH AC 5H 5C KS", "Loss"), + ("AH AC 5H 5C QS", "KH KC 5H 5C QS", "Win"), + ("7C 7S KH 2H 7H", "3C 3S AH 2H 3H", "Win"), + ("3C 3S AH 2H 3H", "7C 7S KH 2H 7H", "Loss"), + ("6H 5H 4H 3H 2H", "5H 4H 3H 2H AH", "Win"), + ("5H 4H 3H 2H AH", "5H 4H 3H 2H AH", "Tie"), + ("5H 4H 3H 2H AH", "6H 5H 4H 3H 2H", "Loss"), + ("AH AD KS KC AC", "AH KD KH AC KC", "Win"), + ("2H 4D 3C AS 5S", "2H 4D 3C 6S 5S", "Loss"), + ("2H 3S 3C 3H 2S", "3S 3C 2S 2H 2D", "Win"), + ("4D 6D 5D 2D JH", "3S 8S 3H TC KH", "Loss"), + ("4S 6C 8S 3S 7S", "AD KS 2D 7D 7C", "Loss"), + ("6S 4C 7H 8C 3H", "5H JC AH 9D 9C", "Loss"), + ("9D 9H JH TC QH", "3C 2S JS 5C 7H", "Win"), + ("2H TC 8S AD 9S", "4H TS 7H 2C 5C", "Win"), + ("9D 3S 2C 7S 7C", "JC TD 3C TC 9H", "Loss"), +) + +TEST_FLUSH = ( + ("2H 3H 4H 5H 6H", True), + ("AS AH 2H AD AC", False), + ("2H 3H 5H 6H 7H", True), + ("KS AS TS QS JS", True), + ("8H 9H QS JS TH", False), + ("AS 3S 4S 8S 2S", True), +) + +TEST_STRAIGHT = ( + ("2H 3H 4H 5H 6H", True), + ("AS AH 2H AD AC", False), + ("2H 3H 5H 6H 7H", False), + ("KS AS TS QS JS", True), + ("8H 9H QS JS TH", True), +) + +TEST_FIVE_HIGH_STRAIGHT = ( + ("2H 4D 3C AS 5S", True, [5, 4, 3, 2, 14]), + ("2H 5D 3C AS 5S", False, [14, 5, 5, 3, 2]), + ("JH QD KC AS TS", False, [14, 13, 12, 11, 10]), + ("9D 3S 2C 7S 7C", False, [9, 7, 7, 3, 2]), +) + +TEST_KIND = ( + ("JH AH TH KH QH", 0), + ("JH 9H TH KH QH", 0), + ("JC KH JS JD JH", 7), + ("KH KC 3S 3H 3D", 6), + ("8C 9C 5C 3C TC", 0), + ("JS QS 9H TS KH", 0), + ("7C 7S KH 2H 7H", 3), + ("3C KH 5D 5S KH", 2), + ("QH 8H KD JH 8S", 1), + ("2D 6D 9D TH 7D", 0), +) + +TEST_TYPES = ( + ("JH AH TH KH QH", 23), + ("JH 9H TH KH QH", 22), + ("JC KH JS JD JH", 21), + ("KH KC 3S 3H 3D", 20), + ("8C 9C 5C 3C TC", 19), + ("JS QS 9H TS KH", 18), + ("7C 7S KH 2H 7H", 17), + ("3C KH 5D 5S KH", 16), + ("QH 8H KD JH 8S", 15), + ("2D 6D 9D TH 7D", 14), +) + + +def generate_random_hand(): + play, oppo = randrange(len(SORTED_HANDS)), randrange(len(SORTED_HANDS)) + expected = ["Loss", "Tie", "Win"][(play >= oppo) + (play > oppo)] + hand, other = SORTED_HANDS[play], SORTED_HANDS[oppo] + return hand, other, expected + + +def generate_random_hands(number_of_hands: int = 100): + return (generate_random_hand() for _ in range(number_of_hands)) + + +@pytest.mark.parametrize("hand, expected", TEST_FLUSH) +def test_hand_is_flush(hand, expected): + assert PokerHand(hand)._is_flush() == expected + + +@pytest.mark.parametrize("hand, expected", TEST_STRAIGHT) +def test_hand_is_straight(hand, expected): + assert PokerHand(hand)._is_straight() == expected + + +@pytest.mark.parametrize("hand, expected, card_values", TEST_FIVE_HIGH_STRAIGHT) +def test_hand_is_five_high_straight(hand, expected, card_values): + player = PokerHand(hand) + assert player._is_five_high_straight() == expected + assert player._card_values == card_values + + +@pytest.mark.parametrize("hand, expected", TEST_KIND) +def test_hand_is_same_kind(hand, expected): + assert PokerHand(hand)._is_same_kind() == expected + + +@pytest.mark.parametrize("hand, expected", TEST_TYPES) +def test_hand_values(hand, expected): + assert PokerHand(hand)._hand_type == expected + + +@pytest.mark.parametrize("hand, other, expected", TEST_COMPARE) +def test_compare_simple(hand, other, expected): + assert PokerHand(hand).compare_with(PokerHand(other)) == expected + + +@pytest.mark.parametrize("hand, other, expected", generate_random_hands()) +def test_compare_random(hand, other, expected): + assert PokerHand(hand).compare_with(PokerHand(other)) == expected + + +def test_hand_sorted(): + POKER_HANDS = [PokerHand(hand) for hand in SORTED_HANDS] + list_copy = POKER_HANDS.copy() + shuffle(list_copy) + user_sorted = chain(sorted(list_copy)) + for index, hand in enumerate(user_sorted): + assert hand == POKER_HANDS[index] + + +def test_custom_sort_five_high_straight(): + # Test that five high straights are compared correctly. + pokerhands = [PokerHand("2D AC 3H 4H 5S"), PokerHand("2S 3H 4H 5S 6C")] + pokerhands.sort(reverse=True) + assert pokerhands[0].__str__() == "2S 3H 4H 5S 6C" + + +def test_multiple_calls_five_high_straight(): + # Multiple calls to five_high_straight function should still return True + # and shouldn't mutate the list in every call other than the first. + pokerhand = PokerHand("2C 4S AS 3D 5C") + expected = True + expected_card_values = [5, 4, 3, 2, 14] + for _ in range(10): + assert pokerhand._is_five_high_straight() == expected + assert pokerhand._card_values == expected_card_values + + +def test_euler_project(): + # Problem number 54 from Project Euler + # Testing from poker_hands.txt file + answer = 0 + script_dir = os.path.abspath(os.path.dirname(__file__)) + poker_hands = os.path.join(script_dir, "poker_hands.txt") + with open(poker_hands, "r") as file_hand: + for line in file_hand: + player_hand = line[:14].strip() + opponent_hand = line[15:].strip() + player, opponent = PokerHand(player_hand), PokerHand(opponent_hand) + output = player.compare_with(opponent) + if output == "Win": + answer += 1 + assert answer == 376 diff --git a/project_euler/problem_97/__init__.py b/project_euler/problem_97/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_97/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_97/sol1.py b/project_euler/problem_97/sol1.py new file mode 100644 index 000000000000..2e848c09a940 --- /dev/null +++ b/project_euler/problem_97/sol1.py @@ -0,0 +1,46 @@ +""" +The first known prime found to exceed one million digits was discovered in 1999, +and is a Mersenne prime of the form 2**6972593 − 1; it contains exactly 2,098,960 +digits. Subsequently other Mersenne primes, of the form 2**p − 1, have been found +which contain more digits. +However, in 2004 there was found a massive non-Mersenne prime which contains +2,357,207 digits: (28433 * (2 ** 7830457 + 1)). + +Find the last ten digits of this prime number. +""" + + +def solution(n: int = 10) -> str: + """ + Returns the last n digits of NUMBER. + >>> solution() + '8739992577' + >>> solution(8) + '39992577' + >>> solution(1) + '7' + >>> solution(-1) + Traceback (most recent call last): + ... + ValueError: Invalid input + >>> solution(8.3) + Traceback (most recent call last): + ... + ValueError: Invalid input + >>> solution("a") + Traceback (most recent call last): + ... + ValueError: Invalid input + """ + if not isinstance(n, int) or n < 0: + raise ValueError("Invalid input") + MODULUS = 10 ** n + NUMBER = 28433 * (pow(2, 7830457, MODULUS)) + 1 + return str(NUMBER % MODULUS) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"{solution(10) = }") diff --git a/scheduling/first_come_first_served.py b/scheduling/first_come_first_served.py index b51fc9fe0c04..c5f61720f97e 100644 --- a/scheduling/first_come_first_served.py +++ b/scheduling/first_come_first_served.py @@ -2,10 +2,10 @@ # In this Algorithm we just care about the order that the processes arrived # without carring about their duration time # https://en.wikipedia.org/wiki/Scheduling_(computing)#First_come,_first_served -from typing import List +from __future__ import annotations -def calculate_waiting_times(duration_times: List[int]) -> List[int]: +def calculate_waiting_times(duration_times: list[int]) -> list[int]: """ This function calculates the waiting time of some processes that have a specified duration time. @@ -24,8 +24,8 @@ def calculate_waiting_times(duration_times: List[int]) -> List[int]: def calculate_turnaround_times( - duration_times: List[int], waiting_times: List[int] -) -> List[int]: + duration_times: list[int], waiting_times: list[int] +) -> list[int]: """ This function calculates the turnaround time of some processes. Return: The time difference between the completion time and the @@ -44,7 +44,7 @@ def calculate_turnaround_times( ] -def calculate_average_turnaround_time(turnaround_times: List[int]) -> float: +def calculate_average_turnaround_time(turnaround_times: list[int]) -> float: """ This function calculates the average of the turnaround times Return: The average of the turnaround times. @@ -58,7 +58,7 @@ def calculate_average_turnaround_time(turnaround_times: List[int]) -> float: return sum(turnaround_times) / len(turnaround_times) -def calculate_average_waiting_time(waiting_times: List[int]) -> float: +def calculate_average_waiting_time(waiting_times: list[int]) -> float: """ This function calculates the average of the waiting times Return: The average of the waiting times. diff --git a/scheduling/round_robin.py b/scheduling/round_robin.py index 4a79301c1816..e8d54dd9a553 100755 --- a/scheduling/round_robin.py +++ b/scheduling/round_robin.py @@ -3,11 +3,12 @@ In Round Robin each process is assigned a fixed time slot in a cyclic way. https://en.wikipedia.org/wiki/Round-robin_scheduling """ +from __future__ import annotations + from statistics import mean -from typing import List -def calculate_waiting_times(burst_times: List[int]) -> List[int]: +def calculate_waiting_times(burst_times: list[int]) -> list[int]: """ Calculate the waiting times of a list of processes that have a specified duration. @@ -40,8 +41,8 @@ def calculate_waiting_times(burst_times: List[int]) -> List[int]: def calculate_turn_around_times( - burst_times: List[int], waiting_times: List[int] -) -> List[int]: + burst_times: list[int], waiting_times: list[int] +) -> list[int]: """ >>> calculate_turn_around_times([1, 2, 3, 4], [0, 1, 3]) [1, 3, 6] diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index ecb6e01fdfe6..f9e2ad975627 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -3,14 +3,14 @@ Please note arrival time and burst Please use spaces to separate times entered. """ -from typing import List +from __future__ import annotations import pandas as pd def calculate_waitingtime( - arrival_time: List[int], burst_time: List[int], no_of_processes: int -) -> List[int]: + arrival_time: list[int], burst_time: list[int], no_of_processes: int +) -> list[int]: """ Calculate the waiting time of each processes Return: list of waiting times. @@ -72,8 +72,8 @@ def calculate_waitingtime( def calculate_turnaroundtime( - burst_time: List[int], no_of_processes: int, waiting_time: List[int] -) -> List[int]: + burst_time: list[int], no_of_processes: int, waiting_time: list[int] +) -> list[int]: """ Calculate the turn around time of each Processes Return: list of turn around times. @@ -91,7 +91,7 @@ def calculate_turnaroundtime( def calculate_average_times( - waiting_time: List[int], turn_around_time: List[int], no_of_processes: int + waiting_time: list[int], turn_around_time: list[int], no_of_processes: int ): """ This function calculates the average of the waiting & turnaround times diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index 01712e8f7707..f09c1527cb0d 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -17,12 +17,17 @@ print(f"{len(space_files)} files contain space characters:") print("\n".join(space_files) + "\n") +hyphen_files = [file for file in filepaths if "-" in file] +if hyphen_files: + print(f"{len(hyphen_files)} files contain hyphen characters:") + print("\n".join(hyphen_files) + "\n") + nodir_files = [file for file in filepaths if os.sep not in file] if nodir_files: print(f"{len(nodir_files)} files are not in a directory:") print("\n".join(nodir_files) + "\n") -bad_files = len(upper_files + space_files + nodir_files) +bad_files = len(upper_files + space_files + hyphen_files + nodir_files) if bad_files: import sys diff --git a/searches/double_linear_search.py b/searches/double_linear_search.py index 6056f00fc2bb..d9dad3c685b6 100644 --- a/searches/double_linear_search.py +++ b/searches/double_linear_search.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def double_linear_search(array: List[int], search_item: int) -> int: +def double_linear_search(array: list[int], search_item: int) -> int: """ Iterate through the array from both sides to find the index of search_item. diff --git a/searches/simple_binary_search.py b/searches/simple_binary_search.py index b6215312fb2d..8495dda8d518 100644 --- a/searches/simple_binary_search.py +++ b/searches/simple_binary_search.py @@ -7,10 +7,10 @@ For manual testing run: python3 simple_binary_search.py """ -from typing import List +from __future__ import annotations -def binary_search(a_list: List[int], item: int) -> bool: +def binary_search(a_list: list[int], item: int) -> bool: """ >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] >>> print(binary_search(test_list, 3)) diff --git a/sorts/iterative_merge_sort.py b/sorts/iterative_merge_sort.py index e6e1513393e0..5ee0badab9e6 100644 --- a/sorts/iterative_merge_sort.py +++ b/sorts/iterative_merge_sort.py @@ -9,10 +9,10 @@ python3 iterative_merge_sort.py """ -from typing import List +from __future__ import annotations -def merge(input_list: List, low: int, mid: int, high: int) -> List: +def merge(input_list: list, low: int, mid: int, high: int) -> list: """ sorting left-half and right-half individually then merging them into result @@ -26,7 +26,7 @@ def merge(input_list: List, low: int, mid: int, high: int) -> List: # iteration over the unsorted list -def iter_merge_sort(input_list: List) -> List: +def iter_merge_sort(input_list: list) -> list: """ Return a sorted copy of the input list diff --git a/sorts/merge_insertion_sort.py b/sorts/merge_insertion_sort.py index 339851699525..fb71d84a3c14 100644 --- a/sorts/merge_insertion_sort.py +++ b/sorts/merge_insertion_sort.py @@ -11,10 +11,10 @@ python3 merge_insertion_sort.py """ -from typing import List +from __future__ import annotations -def merge_insertion_sort(collection: List[int]) -> List[int]: +def merge_insertion_sort(collection: list[int]) -> list[int]: """Pure implementation of merge-insertion sort algorithm in Python :param collection: some mutable ordered collection with heterogeneous diff --git a/sorts/radix_sort.py b/sorts/radix_sort.py index 0ddf996cf1ee..7942462ea10d 100644 --- a/sorts/radix_sort.py +++ b/sorts/radix_sort.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def radix_sort(list_of_ints: List[int]) -> List[int]: +def radix_sort(list_of_ints: list[int]) -> list[int]: """ radix_sort(range(15)) == sorted(range(15)) True diff --git a/sorts/recursive_insertion_sort.py b/sorts/recursive_insertion_sort.py index 5b14c2a6c139..66dd08157df1 100644 --- a/sorts/recursive_insertion_sort.py +++ b/sorts/recursive_insertion_sort.py @@ -2,10 +2,10 @@ A recursive implementation of the insertion sort algorithm """ -from typing import List +from __future__ import annotations -def rec_insertion_sort(collection: List, n: int): +def rec_insertion_sort(collection: list, n: int): """ Given a collection of numbers and its length, sorts the collections in ascending order @@ -36,7 +36,7 @@ def rec_insertion_sort(collection: List, n: int): rec_insertion_sort(collection, n - 1) -def insert_next(collection: List, index: int): +def insert_next(collection: list, index: int): """ Inserts the '(index-1)th' element into place diff --git a/sorts/recursive-quick-sort.py b/sorts/recursive_quick_sort.py similarity index 100% rename from sorts/recursive-quick-sort.py rename to sorts/recursive_quick_sort.py diff --git a/sorts/wiggle_sort.py b/sorts/wiggle_sort.py index 5e5220ffbf05..13bc3ce9606f 100644 --- a/sorts/wiggle_sort.py +++ b/sorts/wiggle_sort.py @@ -9,18 +9,30 @@ """ -def wiggle_sort(nums): - """Perform Wiggle Sort.""" - for i in range(len(nums)): +def wiggle_sort(nums: list) -> list: + """ + Python implementation of wiggle. + Example: + >>> wiggle_sort([0, 5, 3, 2, 2]) + [0, 5, 2, 3, 2] + >>> wiggle_sort([]) + [] + >>> wiggle_sort([-2, -5, -45]) + [-45, -2, -5] + >>> wiggle_sort([-2.1, -5.68, -45.11]) + [-45.11, -2.1, -5.68] + """ + for i, _ in enumerate(nums): if (i % 2 == 1) == (nums[i - 1] > nums[i]): nums[i - 1], nums[i] = nums[i], nums[i - 1] + return nums + if __name__ == "__main__": - print("Enter the array elements:\n") + print("Enter the array elements:") array = list(map(int, input().split())) - print("The unsorted array is:\n") - print(array) - wiggle_sort(array) - print("Array after Wiggle sort:\n") + print("The unsorted array is:") print(array) + print("Array after Wiggle sort:") + print(wiggle_sort(array)) diff --git a/strings/aho-corasick.py b/strings/aho_corasick.py similarity index 100% rename from strings/aho-corasick.py rename to strings/aho_corasick.py diff --git a/strings/can_string_be_rearranged_as_palindrome.py b/strings/can_string_be_rearranged_as_palindrome.py new file mode 100644 index 000000000000..7fedc5877e26 --- /dev/null +++ b/strings/can_string_be_rearranged_as_palindrome.py @@ -0,0 +1,115 @@ +# Created by susmith98 + +from collections import Counter +from timeit import timeit + +# Problem Description: +# Check if characters of the given string can be rearranged to form a palindrome. +# Counter is faster for long strings and non-Counter is faster for short strings. + + +def can_string_be_rearranged_as_palindrome_counter( + input_str: str = "", +) -> bool: + """ + A Palindrome is a String that reads the same forward as it does backwards. + Examples of Palindromes mom, dad, malayalam + >>> can_string_be_rearranged_as_palindrome_counter("Momo") + True + >>> can_string_be_rearranged_as_palindrome_counter("Mother") + False + >>> can_string_be_rearranged_as_palindrome_counter("Father") + False + >>> can_string_be_rearranged_as_palindrome_counter("A man a plan a canal Panama") + True + """ + return sum(c % 2 for c in Counter(input_str.replace(" ", "").lower()).values()) < 2 + + +def can_string_be_rearranged_as_palindrome(input_str: str = "") -> bool: + """ + A Palindrome is a String that reads the same forward as it does backwards. + Examples of Palindromes mom, dad, malayalam + >>> can_string_be_rearranged_as_palindrome("Momo") + True + >>> can_string_be_rearranged_as_palindrome("Mother") + False + >>> can_string_be_rearranged_as_palindrome("Father") + False + >>> can_string_be_rearranged_as_palindrome_counter("A man a plan a canal Panama") + True + """ + if len(input_str) == 0: + return True + lower_case_input_str = input_str.replace(" ", "").lower() + # character_freq_dict: Stores the frequency of every character in the input string + character_freq_dict = {} + + for character in lower_case_input_str: + character_freq_dict[character] = character_freq_dict.get(character, 0) + 1 + """ + Above line of code is equivalent to: + 1) Getting the frequency of current character till previous index + >>> character_freq = character_freq_dict.get(character, 0) + 2) Incrementing the frequency of current character by 1 + >>> character_freq = character_freq + 1 + 3) Updating the frequency of current character + >>> character_freq_dict[character] = character_freq + """ + """ + OBSERVATIONS: + Even length palindrome + -> Every character appears even no.of times. + Odd length palindrome + -> Every character appears even no.of times except for one character. + LOGIC: + Step 1: We'll count number of characters that appear odd number of times i.e oddChar + Step 2:If we find more than 1 character that appears odd number of times, + It is not possible to rearrange as a palindrome + """ + oddChar = 0 + + for character_count in character_freq_dict.values(): + if character_count % 2: + oddChar += 1 + if oddChar > 1: + return False + return True + + +def benchmark(input_str: str = "") -> None: + """ + Benchmark code for comparing above 2 functions + """ + print("\nFor string = ", input_str, ":") + print( + "> can_string_be_rearranged_as_palindrome_counter()", + "\tans =", + can_string_be_rearranged_as_palindrome_counter(input_str), + "\ttime =", + timeit( + "z.can_string_be_rearranged_as_palindrome_counter(z.check_str)", + setup="import __main__ as z", + ), + "seconds", + ) + print( + "> can_string_be_rearranged_as_palindrome()", + "\tans =", + can_string_be_rearranged_as_palindrome(input_str), + "\ttime =", + timeit( + "z.can_string_be_rearranged_as_palindrome(z.check_str)", + setup="import __main__ as z", + ), + "seconds", + ) + + +if __name__ == "__main__": + check_str = input( + "Enter string to determine if it can be rearranged as a palindrome or not: " + ).strip() + benchmark(check_str) + status = can_string_be_rearranged_as_palindrome_counter(check_str) + print(f"{check_str} can {'' if status else 'not '}be rearranged as a palindrome") diff --git a/strings/check_anagrams.py b/strings/check_anagrams.py index 7cc1e2978db9..3083000cbb5d 100644 --- a/strings/check_anagrams.py +++ b/strings/check_anagrams.py @@ -1,4 +1,9 @@ -def check_anagrams(a: str, b: str) -> bool: +""" +wiki: https://en.wikipedia.org/wiki/Anagram +""" + + +def check_anagrams(first_str: str, second_str: str) -> bool: """ Two strings are anagrams if they are made of the same letters arranged differently (ignoring the case). @@ -6,13 +11,21 @@ def check_anagrams(a: str, b: str) -> bool: True >>> check_anagrams('This is a string', 'Is this a string') True + >>> check_anagrams('This is a string', 'Is this a string') + True >>> check_anagrams('There', 'Their') False """ - return sorted(a.lower()) == sorted(b.lower()) + return ( + "".join(sorted(first_str.lower())).strip() + == "".join(sorted(second_str.lower())).strip() + ) if __name__ == "__main__": + from doctest import testmod + + testmod() input_A = input("Enter the first string ").strip() input_B = input("Enter the second string ").strip() diff --git a/strings/lower.py b/strings/lower.py index a1bad44c7403..b7abe9fc957d 100644 --- a/strings/lower.py +++ b/strings/lower.py @@ -1,5 +1,4 @@ def lower(word: str) -> str: - """ Will convert the entire string to lowecase letters @@ -9,7 +8,6 @@ def lower(word: str) -> str: 'hellzo' >>> lower("WHAT") 'what' - >>> lower("wh[]32") 'wh[]32' >>> lower("whAT") @@ -19,9 +17,7 @@ def lower(word: str) -> str: # converting to ascii value int value and checking to see if char is a capital # letter if it is a capital letter it is getting shift by 32 which makes it a lower # case letter - return "".join( - chr(ord(char) + 32) if 65 <= ord(char) <= 90 else char for char in word - ) + return "".join(chr(ord(char) + 32) if "A" <= char <= "Z" else char for char in word) if __name__ == "__main__": diff --git a/strings/remove_duplicate.py b/strings/remove_duplicate.py index 6357050ac17d..5ab0e9962752 100644 --- a/strings/remove_duplicate.py +++ b/strings/remove_duplicate.py @@ -1,14 +1,15 @@ -""" Created by sarathkaul on 14/11/19 """ - - def remove_duplicates(sentence: str) -> str: """ - Reomove duplicates from sentence + Remove duplicates from sentence >>> remove_duplicates("Python is great and Java is also great") 'Java Python also and great is' + >>> remove_duplicates("Python is great and Java is also great") + 'Java Python also and great is' """ - return " ".join(sorted(set(sentence.split(" ")))) + return " ".join(sorted(set(sentence.split()))) if __name__ == "__main__": - print(remove_duplicates("INPUT_SENTENCE")) + import doctest + + doctest.testmod() diff --git a/strings/reverse_words.py b/strings/reverse_words.py index 8ab060fe1d24..504c1c2089dd 100644 --- a/strings/reverse_words.py +++ b/strings/reverse_words.py @@ -1,18 +1,15 @@ -# Created by sarathkaul on 18/11/19 -# Edited by farnswj1 on 4/4/20 - - def reverse_words(input_str: str) -> str: """ Reverses words in a given string - >>> sentence = "I love Python" - >>> reverse_words(sentence) == " ".join(sentence.split()[::-1]) - True - >>> reverse_words(sentence) + >>> reverse_words("I love Python") 'Python love I' + >>> reverse_words("I Love Python") + 'Python Love I' """ - return " ".join(reversed(input_str.split(" "))) + return " ".join(input_str.split()[::-1]) if __name__ == "__main__": - print(reverse_words("INPUT STRING")) + import doctest + + doctest.testmod() diff --git a/strings/upper.py b/strings/upper.py index 2c264c641b12..411802a2a22f 100644 --- a/strings/upper.py +++ b/strings/upper.py @@ -8,7 +8,6 @@ def upper(word: str) -> str: 'HELLO' >>> upper("WHAT") 'WHAT' - >>> upper("wh[]32") 'WH[]32' """ @@ -16,9 +15,7 @@ def upper(word: str) -> str: # converting to ascii value int value and checking to see if char is a lower letter # if it is a capital letter it is getting shift by 32 which makes it a capital case # letter - return "".join( - chr(ord(char) - 32) if 97 <= ord(char) <= 122 else char for char in word - ) + return "".join(chr(ord(char) - 32) if "a" <= char <= "z" else char for char in word) if __name__ == "__main__": diff --git a/traversals/binary_tree_traversals.py b/traversals/binary_tree_traversals.py index 50cdd5af72d3..cb471ba55bac 100644 --- a/traversals/binary_tree_traversals.py +++ b/traversals/binary_tree_traversals.py @@ -3,8 +3,9 @@ """ This is pure Python implementation of tree traversal algorithms """ +from __future__ import annotations + import queue -from typing import List class TreeNode: diff --git a/web_programming/fetch_jobs.py b/web_programming/fetch_jobs.py index 888f41294974..bb2171e1f0ee 100644 --- a/web_programming/fetch_jobs.py +++ b/web_programming/fetch_jobs.py @@ -1,7 +1,9 @@ """ Scraping jobs given job title and location from indeed website """ -from typing import Generator, Tuple +from __future__ import annotations + +from typing import Generator import requests from bs4 import BeautifulSoup @@ -9,7 +11,7 @@ url = "https://www.indeed.co.in/jobs?q=mobile+app+development&l=" -def fetch_jobs(location: str = "mumbai") -> Generator[Tuple[str, str], None, None]: +def fetch_jobs(location: str = "mumbai") -> Generator[tuple[str, str], None, None]: soup = BeautifulSoup(requests.get(url + location).content, "html.parser") # This attribute finds out all the specifics listed in a job for job in soup.find_all("div", attrs={"data-tn-component": "organicJob"}): diff --git a/web_programming/get_imdb_top_250_movies_csv.py b/web_programming/get_imdb_top_250_movies_csv.py index 811c21fb00e4..e54b076ebd94 100644 --- a/web_programming/get_imdb_top_250_movies_csv.py +++ b/web_programming/get_imdb_top_250_movies_csv.py @@ -1,11 +1,12 @@ +from __future__ import annotations + import csv -from typing import Dict import requests from bs4 import BeautifulSoup -def get_imdb_top_250_movies(url: str = "") -> Dict[str, float]: +def get_imdb_top_250_movies(url: str = "") -> dict[str, float]: url = url or "https://www.imdb.com/chart/top/?ref_=nv_mv_250" soup = BeautifulSoup(requests.get(url).text, "html.parser") titles = soup.find_all("td", attrs="titleColumn")